Blender V5.0
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#include "BLI_string_utf8.h"
20
21#include "BLT_translation.hh"
22
23#include "DNA_modifier_types.h"
25
26#include "ED_object.hh"
27#include "ED_screen.hh"
28#include "ED_undo.hh"
29
30#include "MOD_nodes.hh"
31#include "NOD_geometry.hh"
35
36#include "RNA_access.hh"
37#include "RNA_prototypes.hh"
38
39#include "UI_interface.hh"
41#include "UI_resources.hh"
42
44
45namespace blender::nodes {
46
47namespace geo_log = geo_eval_log;
48
49namespace {
50struct PanelOpenProperty {
51 PointerRNA ptr;
52 StringRefNull name;
53};
54
55struct SearchInfo {
56 geo_log::GeoTreeLog *tree_log = nullptr;
57 bNodeTree *tree = nullptr;
58 IDProperty *properties = nullptr;
59};
60
61struct ModifierSearchData {
62 uint32_t object_session_uid;
64};
65
66struct OperatorSearchData {
68 SearchInfo info;
69};
70
71struct SocketSearchData {
72 std::variant<ModifierSearchData, OperatorSearchData> search_data;
73 char socket_identifier[MAX_NAME];
74 bool is_output;
75
76 SearchInfo info(const bContext &C) const;
77};
78/* This class must not have a destructor, since it is used by buttons and freed with #MEM_freeN. */
79BLI_STATIC_ASSERT(std::is_trivially_destructible_v<SocketSearchData>, "");
80
81struct DrawGroupInputsContext {
82 const bContext &C;
84 geo_log::GeoTreeLog *tree_log;
85 IDProperty *properties;
86 PointerRNA *properties_ptr;
87 PointerRNA *bmain_ptr;
88 Array<nodes::socket_usage_inference::SocketUsage> input_usages;
89 Array<nodes::socket_usage_inference::SocketUsage> output_usages;
90 bool use_name_for_ids = false;
91 std::function<PanelOpenProperty(const bNodeTreeInterfacePanel &)> panel_open_property_fn;
92 std::function<SocketSearchData(const bNodeTreeInterfaceSocket &)> socket_search_data_fn;
93 std::function<void(uiLayout &, int icon, const bNodeTreeInterfaceSocket &)>
94 draw_attribute_toggle_fn;
95
96 bool input_is_visible(const bNodeTreeInterfaceSocket &socket) const
97 {
98 return this->input_usages[this->tree->interface_input_index(socket)].is_visible;
99 }
100
101 bool input_is_active(const bNodeTreeInterfaceSocket &socket) const
102 {
103 return this->input_usages[this->tree->interface_input_index(socket)].is_used;
104 }
105};
106} // namespace
107
109{
110 if (!nmd.runtime->eval_log) {
111 return nullptr;
112 }
113 bke::ModifierComputeContext compute_context{nullptr, nmd};
114 return &nmd.runtime->eval_log->get_tree_log(compute_context.hash());
115}
116
118 const wmWindowManager &wm,
119 const ModifierSearchData &data)
120{
122 /* Work around an issue where the attribute search exec function has stale pointers when data
123 * is reallocated when evaluating the node tree, causing a crash. This would be solved by
124 * allowing the UI search data to own arbitrary memory rather than just referencing it. */
125 return nullptr;
126 }
127
129 &bmain, ID_OB, data.object_session_uid);
130 if (object == nullptr) {
131 return nullptr;
132 }
133 ModifierData *md = BKE_modifiers_findby_name(object, data.modifier_name);
134 if (md == nullptr) {
135 return nullptr;
136 }
138 return reinterpret_cast<NodesModifierData *>(md);
139}
140
141SearchInfo SocketSearchData::info(const bContext &C) const
142{
143 if (const auto *modifier_search_data = std::get_if<ModifierSearchData>(&this->search_data)) {
144 const NodesModifierData *nmd = get_modifier_data(
145 *CTX_data_main(&C), *CTX_wm_manager(&C), *modifier_search_data);
146 if (nmd == nullptr) {
147 return {};
148 }
149 if (nmd->node_group == nullptr) {
150 return {};
151 }
152 geo_log::GeoTreeLog *tree_log = get_root_tree_log(*nmd);
153 return {tree_log, nmd->node_group, nmd->settings.properties};
154 }
155 if (const auto *operator_search_data = std::get_if<OperatorSearchData>(&this->search_data)) {
156 return operator_search_data->info;
157 }
158 return {};
159}
160
162 const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
163{
164 const SocketSearchData &data = *static_cast<SocketSearchData *>(arg);
165 const SearchInfo info = data.info(*C);
166 if (!info.tree || !info.tree_log) {
167 return;
168 }
169 info.tree_log->ensure_layer_names();
170 info.tree->ensure_topology_cache();
171
172 Vector<const bNodeSocket *> sockets_to_check;
173 for (const bNode *node : info.tree->group_input_nodes()) {
174 for (const bNodeSocket *socket : node->output_sockets()) {
175 if (socket->type == SOCK_GEOMETRY) {
176 sockets_to_check.append(socket);
177 }
178 }
179 }
180
181 Set<StringRef> names;
182 Vector<const std::string *> layer_names;
183 for (const bNodeSocket *socket : sockets_to_check) {
184 const geo_log::ValueLog *value_log = info.tree_log->find_socket_value_log(*socket);
185 if (value_log == nullptr) {
186 continue;
187 }
188 if (const auto *geo_log = dynamic_cast<const geo_log::GeometryInfoLog *>(value_log)) {
189 if (const std::optional<geo_log::GeometryInfoLog::GreasePencilInfo> &grease_pencil_info =
190 geo_log->grease_pencil_info)
191 {
192 for (const std::string &name : grease_pencil_info->layer_names) {
193 if (names.add(name)) {
194 layer_names.append(&name);
195 }
196 }
197 }
198 }
199 }
200 BLI_assert(items);
201 ui::grease_pencil_layer_search_add_items(str, layer_names.as_span(), *items, is_first);
202}
203
204static void layer_name_search_exec_fn(bContext *C, void *data_v, void *item_v)
205{
206 const SocketSearchData &data = *static_cast<SocketSearchData *>(data_v);
207 const std::string *item = static_cast<std::string *>(item_v);
208 if (!item) {
209 return;
210 }
211 const SearchInfo info = data.info(*C);
212 if (!info.properties) {
213 return;
214 }
215
216 IDProperty &name_property = *IDP_GetPropertyFromGroup(info.properties, data.socket_identifier);
217 IDP_AssignString(&name_property, item->c_str());
218
219 ED_undo_push(C, "Assign Layer Name");
220}
221
222static void add_layer_name_search_button(DrawGroupInputsContext &ctx,
223 uiLayout *layout,
224 const bNodeTreeInterfaceSocket &socket)
225{
226 const std::string rna_path = fmt::format("[\"{}\"]", BLI_str_escape(socket.identifier));
227 if (!ctx.tree_log) {
228 layout->prop(ctx.properties_ptr, rna_path, UI_ITEM_NONE, "", ICON_NONE);
229 return;
230 }
231
232 layout->use_property_decorate_set(false);
233
234 uiLayout *split = &layout->split(0.4f, false);
235 uiLayout *name_row = &split->row(false);
237
238 name_row->label(socket.name ? IFACE_(socket.name) : "", ICON_NONE);
239 uiLayout *prop_row = &split->row(true);
240
241 uiBlock *block = prop_row->block();
242 uiBut *but = uiDefIconTextButR(block,
244 0,
245 ICON_OUTLINER_DATA_GP_LAYER,
246 "",
247 0,
248 0,
249 10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */
250 UI_UNIT_Y,
251 ctx.properties_ptr,
252 rna_path,
253 0,
254 StringRef(socket.description));
255 UI_but_placeholder_set(but, IFACE_("Layer"));
256 layout->label("", ICON_BLANK1);
257
258 const Object *object = ed::object::context_object(&ctx.C);
259 BLI_assert(object != nullptr);
260 if (object == nullptr) {
261 return;
262 }
263
264 /* Using a custom free function make the search not work currently. So make sure this data can be
265 * freed with MEM_freeN. */
266 SocketSearchData *data = static_cast<SocketSearchData *>(
267 MEM_mallocN(sizeof(SocketSearchData), __func__));
268 *data = ctx.socket_search_data_fn(socket);
272 nullptr,
274 data,
275 true,
276 nullptr,
278 nullptr);
279}
280
282 const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
283{
284 SocketSearchData &data = *static_cast<SocketSearchData *>(arg);
285 const SearchInfo info = data.info(*C);
286 if (!info.tree || !info.tree_log) {
287 return;
288 }
289 info.tree_log->ensure_existing_attributes();
290 info.tree->ensure_topology_cache();
291
292 Vector<const bNodeSocket *> sockets_to_check;
293 if (data.is_output) {
294 for (const bNode *node : info.tree->nodes_by_type("NodeGroupOutput")) {
295 for (const bNodeSocket *socket : node->input_sockets()) {
296 if (socket->type == SOCK_GEOMETRY) {
297 sockets_to_check.append(socket);
298 }
299 }
300 }
301 }
302 else {
303 for (const bNode *node : info.tree->group_input_nodes()) {
304 for (const bNodeSocket *socket : node->output_sockets()) {
305 if (socket->type == SOCK_GEOMETRY) {
306 sockets_to_check.append(socket);
307 }
308 }
309 }
310 }
311 Set<StringRef> names;
313 for (const bNodeSocket *socket : sockets_to_check) {
314 const geo_log::ValueLog *value_log = info.tree_log->find_socket_value_log(*socket);
315 if (value_log == nullptr) {
316 continue;
317 }
318 if (const auto *geo_log = dynamic_cast<const geo_log::GeometryInfoLog *>(value_log)) {
319 for (const geo_log::GeometryAttributeInfo &attribute : geo_log->attributes) {
320 if (names.add(attribute.name)) {
321 attributes.append(&attribute);
322 }
323 }
324 }
325 }
326 ui::attribute_search_add_items(str, data.is_output, attributes.as_span(), items, is_first);
327}
328
329static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
330{
331 if (item_v == nullptr) {
332 return;
333 }
334 SocketSearchData &data = *static_cast<SocketSearchData *>(data_v);
335 const auto &item = *static_cast<const geo_log::GeometryAttributeInfo *>(item_v);
336 const SearchInfo info = data.info(*C);
337 if (!info.properties) {
338 return;
339 }
340
341 const std::string attribute_prop_name = data.socket_identifier +
343 IDProperty &name_property = *IDP_GetPropertyFromGroup(info.properties, attribute_prop_name);
344 IDP_AssignString(&name_property, item.name.c_str());
345
346 ED_undo_push(C, "Assign Attribute Name");
347}
348
349static void add_attribute_search_button(DrawGroupInputsContext &ctx,
350 uiLayout *layout,
351 const StringRefNull rna_path_attribute_name,
352 const bNodeTreeInterfaceSocket &socket)
353{
354 if (!ctx.tree_log) {
355 layout->prop(ctx.properties_ptr, rna_path_attribute_name, UI_ITEM_NONE, "", ICON_NONE);
356 return;
357 }
358
359 uiBlock *block = layout->block();
360 uiBut *but = uiDefIconTextButR(block,
362 0,
363 ICON_NONE,
364 "",
365 0,
366 0,
367 10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */
368 UI_UNIT_Y,
369 ctx.properties_ptr,
370 rna_path_attribute_name,
371 0,
372 StringRef(socket.description));
373
374 const Object *object = ed::object::context_object(&ctx.C);
375 BLI_assert(object != nullptr);
376 if (object == nullptr) {
377 return;
378 }
379
380 /* Using a custom free function make the search not work currently. So make sure this data can be
381 * freed with MEM_freeN. */
382 SocketSearchData *data = static_cast<SocketSearchData *>(
383 MEM_mallocN(sizeof(SocketSearchData), __func__));
384 *data = ctx.socket_search_data_fn(socket);
388 nullptr,
390 data,
391 true,
392 nullptr,
394 nullptr);
395
396 std::string attribute_name = RNA_string_get(ctx.properties_ptr, rna_path_attribute_name.c_str());
397 const bool access_allowed = bke::allow_procedural_attribute_access(attribute_name);
398 if (!access_allowed) {
400 }
401}
402
404 DrawGroupInputsContext &ctx,
405 uiLayout *layout,
406 const StringRefNull rna_path,
407 const bNodeTreeInterfaceSocket &socket,
408 const std::optional<StringRefNull> use_name = std::nullopt)
409{
410 const bke::bNodeSocketType *typeinfo = socket.socket_typeinfo();
411 const eNodeSocketDatatype type = typeinfo ? typeinfo->type : SOCK_CUSTOM;
412 const std::string rna_path_attribute_name = fmt::format(
414
415 /* We're handling this manually in this case. */
416 layout->use_property_decorate_set(false);
417
418 uiLayout *split = &layout->split(0.4f, false);
419 uiLayout *name_row = &split->row(false);
421
422 uiLayout *prop_row = nullptr;
423
424 const std::optional<StringRef> attribute_name = nodes::input_attribute_name_get(ctx.properties,
425 socket);
426 const StringRefNull socket_name = use_name.has_value() ?
427 (*use_name) :
428 (socket.name ? IFACE_(socket.name) : "");
429 if (type == SOCK_BOOLEAN && !attribute_name) {
430 name_row->label("", ICON_NONE);
431 prop_row = &split->row(true);
432 }
433 else {
434 prop_row = &layout->row(true);
435 }
436
437 if (type == SOCK_BOOLEAN) {
438 prop_row->use_property_split_set(false);
440 }
441
442 if (attribute_name) {
443 name_row->label(IFACE_(socket_name), ICON_NONE);
444 prop_row = &split->row(true);
445 add_attribute_search_button(ctx, prop_row, rna_path_attribute_name, socket);
446 layout->label("", ICON_BLANK1);
447 }
448 else {
449 const char *name = IFACE_(socket_name.c_str());
450 prop_row->prop(ctx.properties_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE);
451 layout->decorator(ctx.properties_ptr, rna_path.c_str(), -1);
452 }
453
454 ctx.draw_attribute_toggle_fn(*prop_row, ICON_SPREADSHEET, socket);
455}
456
458{
459 for (const int i : IndexRange(nmd.panels_num)) {
460 if (nmd.panels[i].id == id) {
461 return &nmd.panels[i];
462 }
463 }
464 return nullptr;
465}
466
467/* Drawing the properties manually with #uiLayout::prop instead of #uiDefAutoButsRNA allows using
468 * the node socket identifier for the property names, since they are unique, but also having
469 * the correct label displayed in the UI. */
470static void draw_property_for_socket(DrawGroupInputsContext &ctx,
471 uiLayout *layout,
472 const bNodeTreeInterfaceSocket &socket,
473 const std::optional<StringRef> parent_name = std::nullopt)
474{
475 const StringRefNull identifier = socket.identifier;
476 /* The property should be created in #MOD_nodes_update_interface with the correct type. */
477 IDProperty *property = IDP_GetPropertyFromGroup_null(ctx.properties, identifier);
478
479 /* IDProperties can be removed with python, so there could be a situation where
480 * there isn't a property for a socket or it doesn't have the correct type. */
481 if (property == nullptr ||
482 !nodes::id_property_type_matches_socket(socket, *property, ctx.use_name_for_ids))
483 {
484 return;
485 }
486
487 const int input_index = ctx.tree->interface_input_index(socket);
488 if (!ctx.input_is_visible(socket)) {
489 /* The input is not used currently, but it would be used if any menu input is changed.
490 * By convention, the input is hidden in this case instead of just grayed out. */
491 return;
492 }
493
494 uiLayout *row = &layout->row(true);
495 row->use_property_decorate_set(true);
496 row->active_set(ctx.input_is_active(socket));
497
498 const std::string rna_path = fmt::format("[\"{}\"]", BLI_str_escape(identifier.c_str()));
499
500 /* Use #uiLayout::prop_search to draw pointer properties because #uiLayout::prop would not have
501 * enough information about what type of ID to select for editing the values. This is because
502 * pointer IDProperties contain no information about their type. */
503 const bke::bNodeSocketType *typeinfo = socket.socket_typeinfo();
504 const eNodeSocketDatatype type = typeinfo ? typeinfo->type : SOCK_CUSTOM;
505 std::string name = socket.name ? IFACE_(socket.name) : "";
506
507 /* If the property has a prefix that's the same string as the name of the panel it's in, remove
508 * the prefix so it appears less verbose. */
509 if (parent_name.has_value()) {
510 const StringRef prefix_to_remove = *parent_name;
511 const int prefix_size = prefix_to_remove.size();
512 const int pos = name.find(prefix_to_remove);
513 if (pos == 0 && name.size() > prefix_size && name[prefix_size] == ' ') {
514 name = name.substr(prefix_size + 1);
515 }
516 }
517
518 switch (type) {
519 case SOCK_OBJECT: {
520 row->prop_search(
521 ctx.properties_ptr, rna_path, ctx.bmain_ptr, "objects", name, ICON_OBJECT_DATA);
522 break;
523 }
524 case SOCK_COLLECTION: {
525 row->prop_search(ctx.properties_ptr,
526 rna_path,
527 ctx.bmain_ptr,
528 "collections",
529 name,
530 ICON_OUTLINER_COLLECTION);
531 break;
532 }
533 case SOCK_MATERIAL: {
534 row->prop_search(
535 ctx.properties_ptr, rna_path, ctx.bmain_ptr, "materials", name, ICON_MATERIAL);
536 break;
537 }
538 case SOCK_TEXTURE: {
539 row->prop_search(
540 ctx.properties_ptr, rna_path, ctx.bmain_ptr, "textures", name, ICON_TEXTURE);
541 break;
542 }
543 case SOCK_IMAGE: {
544 PropertyRNA *prop = RNA_struct_find_property(ctx.properties_ptr, rna_path.c_str());
545 if (prop && RNA_property_type(prop) == PROP_POINTER) {
546 uiTemplateID(row,
547 &ctx.C,
548 ctx.properties_ptr,
549 rna_path,
550 "image.new",
551 "image.open",
552 nullptr,
554 false,
555 name);
556 }
557 else {
558 /* #uiTemplateID only supports pointer properties currently. Node tools store data-block
559 * pointers in strings currently. */
560 row->prop_search(ctx.properties_ptr, rna_path, ctx.bmain_ptr, "images", name, ICON_IMAGE);
561 }
562 break;
563 }
564 case SOCK_MENU: {
566 /* Use a single space when the name is empty to work around a bug with expanded enums. Also
567 * see #ui_item_enum_expand_exec. */
568 row->prop(ctx.properties_ptr,
569 rna_path,
571 StringRef(name).is_empty() ? " " : name,
572 ICON_NONE);
573 }
574 else {
575 row->prop(ctx.properties_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE);
576 }
577 break;
578 }
579 case SOCK_BOOLEAN: {
580 if (is_layer_selection_field(socket)) {
581 add_layer_name_search_button(ctx, row, socket);
582 /* Adds a spacing at the end of the row. */
583 row->label("", ICON_BLANK1);
584 break;
585 }
587 }
588 default: {
589 if (nodes::input_has_attribute_toggle(*ctx.tree, input_index)) {
590 add_attribute_search_or_value_buttons(ctx, row, rna_path, socket, name);
591 }
592 else {
593 row->prop(ctx.properties_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE);
594 }
595 }
596 }
597 if (!nodes::input_has_attribute_toggle(*ctx.tree, input_index)) {
598 row->label("", ICON_BLANK1);
599 }
600}
601
602static bool interface_panel_has_socket(DrawGroupInputsContext &ctx,
603 const bNodeTreeInterfacePanel &interface_panel)
604{
605 for (const bNodeTreeInterfaceItem *item : interface_panel.items()) {
606 if (item->item_type == NODE_INTERFACE_SOCKET) {
607 const bNodeTreeInterfaceSocket &socket = *reinterpret_cast<const bNodeTreeInterfaceSocket *>(
608 item);
610 continue;
611 }
612 if (socket.flag & NODE_INTERFACE_SOCKET_INPUT) {
613 if (ctx.input_is_visible(socket)) {
614 return true;
615 }
616 }
617 }
618 else if (item->item_type == NODE_INTERFACE_PANEL) {
620 *reinterpret_cast<const bNodeTreeInterfacePanel *>(item)))
621 {
622 return true;
623 }
624 }
625 }
626 return false;
627}
628
629static bool interface_panel_affects_output(DrawGroupInputsContext &ctx,
630 const bNodeTreeInterfacePanel &panel)
631{
632 for (const bNodeTreeInterfaceItem *item : panel.items()) {
633 if (item->item_type == NODE_INTERFACE_SOCKET) {
634 const auto &socket = *reinterpret_cast<const bNodeTreeInterfaceSocket *>(item);
635 if (socket.flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER) {
636 continue;
637 }
638 if (!(socket.flag & NODE_INTERFACE_SOCKET_INPUT)) {
639 continue;
640 }
641 if (ctx.input_is_active(socket)) {
642 return true;
643 }
644 }
645 else if (item->item_type == NODE_INTERFACE_PANEL) {
646 const auto &sub_interface_panel = *reinterpret_cast<const bNodeTreeInterfacePanel *>(item);
647 if (interface_panel_affects_output(ctx, sub_interface_panel)) {
648 return true;
649 }
650 }
651 }
652 return false;
653}
654
656 DrawGroupInputsContext &ctx,
657 uiLayout *layout,
658 const bNodeTreeInterfacePanel &interface_panel,
659 const bool skip_first = false,
660 const std::optional<StringRef> parent_name = std::nullopt);
661
662static void draw_interface_panel_as_panel(DrawGroupInputsContext &ctx,
663 uiLayout &layout,
664 const bNodeTreeInterfacePanel &interface_panel)
665{
666 if (!interface_panel_has_socket(ctx, interface_panel)) {
667 return;
668 }
669 PanelOpenProperty open_property = ctx.panel_open_property_fn(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 = interface_panel.header_toggle_socket();
674 const StringRef panel_name = interface_panel.name;
675 if (toggle_socket && !(toggle_socket->flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER)) {
676 const StringRefNull identifier = toggle_socket->identifier;
677 IDProperty *property = IDP_GetPropertyFromGroup_null(ctx.properties, identifier);
678 /* IDProperties can be removed with python, so there could be a situation where
679 * there isn't a property for a socket or it doesn't have the correct type. */
680 if (property == nullptr ||
681 !nodes::id_property_type_matches_socket(*toggle_socket, *property, ctx.use_name_for_ids))
682 {
683 return;
684 }
685 const std::string rna_path = fmt::format("[\"{}\"]", BLI_str_escape(identifier.c_str()));
686 panel_layout = layout.panel_prop_with_bool_header(&ctx.C,
687 &open_property.ptr,
688 open_property.name,
689 ctx.properties_ptr,
690 rna_path,
691 IFACE_(panel_name));
692 skip_first = true;
693 }
694 else {
695 panel_layout = layout.panel_prop(&ctx.C, &open_property.ptr, open_property.name);
696 panel_layout.header->label(IFACE_(panel_name), ICON_NONE);
697 }
698 if (!interface_panel_affects_output(ctx, interface_panel)) {
699 panel_layout.header->active_set(false);
700 }
702 panel_layout.header,
703 [](bContext * /*C*/, void *panel_arg, const StringRef /*tip*/) -> std::string {
704 const auto *panel = static_cast<bNodeTreeInterfacePanel *>(panel_arg);
705 return StringRef(panel->description);
706 },
707 const_cast<bNodeTreeInterfacePanel *>(&interface_panel),
708 nullptr,
709 nullptr);
710 if (panel_layout.body) {
711 draw_interface_panel_content(ctx, panel_layout.body, interface_panel, skip_first, panel_name);
712 }
713}
714
715static void draw_interface_panel_content(DrawGroupInputsContext &ctx,
716 uiLayout *layout,
717 const bNodeTreeInterfacePanel &interface_panel,
718 const bool skip_first,
719 const std::optional<StringRef> parent_name)
720{
721 for (const bNodeTreeInterfaceItem *item : interface_panel.items().drop_front(skip_first ? 1 : 0))
722 {
723 switch (NodeTreeInterfaceItemType(item->item_type)) {
725 const auto &sub_interface_panel = *reinterpret_cast<const bNodeTreeInterfacePanel *>(item);
726 draw_interface_panel_as_panel(ctx, *layout, sub_interface_panel);
727 break;
728 }
730 const auto &interface_socket = *reinterpret_cast<const bNodeTreeInterfaceSocket *>(item);
731 if (interface_socket.flag & NODE_INTERFACE_SOCKET_INPUT) {
732 if (!(interface_socket.flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER)) {
733 draw_property_for_socket(ctx, layout, interface_socket, parent_name);
734 }
735 }
736 break;
737 }
738 }
739 }
740}
741
742static std::string get_node_warning_panel_name(const int num_errors,
743 const int num_warnings,
744 const int num_infos)
745{
746 fmt::memory_buffer buffer;
747 fmt::appender buf = fmt::appender(buffer);
748 if (num_errors > 0) {
749 fmt::format_to(buf, "{} ({})", IFACE_("Errors"), num_errors);
750 }
751 if (num_warnings > 0) {
752 if (num_errors > 0) {
753 fmt::format_to(buf, ", ");
754 }
755 fmt::format_to(buf, "{} ({})", IFACE_("Warnings"), num_warnings);
756 }
757 if (num_infos > 0) {
758 if (num_errors > 0 || num_warnings > 0) {
759 fmt::format_to(buf, ", ");
760 }
761 fmt::format_to(buf, "{} ({})", IFACE_("Info"), num_infos);
762 }
763 return std::string(buffer.data(), buffer.size());
764}
765
766static void draw_warnings(const bContext *C,
767 const NodesModifierData &nmd,
768 uiLayout *layout,
769 PointerRNA *md_ptr)
770{
771 if (G.is_rendering) {
772 /* Avoid accessing this data while baking in a separate thread. */
773 return;
774 }
775 using namespace geo_log;
776 GeoTreeLog *tree_log = get_root_tree_log(nmd);
777 if (!tree_log) {
778 return;
779 }
780 tree_log->ensure_node_warnings(*CTX_data_main(C));
781 const int warnings_num = tree_log->all_warnings.size();
782 if (warnings_num == 0) {
783 return;
784 }
785 Map<NodeWarningType, int> count_by_type;
786 for (const NodeWarning &warning : tree_log->all_warnings) {
787 count_by_type.lookup_or_add(warning.type, 0)++;
788 }
789 const int num_errors = count_by_type.lookup_default(NodeWarningType::Error, 0);
790 const int num_warnings = count_by_type.lookup_default(NodeWarningType::Warning, 0);
791 const int num_infos = count_by_type.lookup_default(NodeWarningType::Info, 0);
792 const std::string panel_name = get_node_warning_panel_name(num_errors, num_warnings, num_infos);
793 PanelLayout panel = layout->panel_prop(C, md_ptr, "open_warnings_panel");
794 panel.header->label(panel_name.c_str(), ICON_NONE);
795 if (!panel.body) {
796 return;
797 }
798 Vector<const NodeWarning *> warnings(tree_log->all_warnings.size());
799 for (const int i : warnings.index_range()) {
800 warnings[i] = &tree_log->all_warnings[i];
801 }
802 std::sort(warnings.begin(), warnings.end(), [](const NodeWarning *a, const NodeWarning *b) {
803 const int severity_a = node_warning_type_severity(a->type);
804 const int severity_b = node_warning_type_severity(b->type);
805 if (severity_a > severity_b) {
806 return true;
807 }
808 if (severity_a < severity_b) {
809 return false;
810 }
811 return BLI_strcasecmp_natural(a->message.c_str(), b->message.c_str()) < 0;
812 });
813
814 uiLayout *col = &panel.body->column(false);
815 uiBlock *block = col->block();
816 for (const NodeWarning *warning : warnings) {
817 const int icon = node_warning_type_icon(warning->type);
818 const StringRef message = RPT_(warning->message);
819 uiBut *but = uiDefIconTextBut(
820 block, ButType::Label, 0, icon, message, 0, 0, 1, UI_UNIT_Y, nullptr, std::nullopt);
821 /* Add tooltip containing the same message. This is helpful if the message is very long so that
822 * it doesn't fit in the panel. */
824 but,
825 [](bContext * /*C*/, void *argN, blender::StringRef /*tip*/) -> std::string {
826 return *static_cast<std::string *>(argN);
827 },
828 MEM_new<std::string>(__func__, message),
829 [](void *arg) { MEM_delete(static_cast<std::string *>(arg)); });
830 }
831}
832
834{
835 if (!tree) {
836 return false;
837 }
838 for (const bNodeTreeInterfaceSocket *interface_socket : tree->interface_outputs()) {
839 const bke::bNodeSocketType *typeinfo = interface_socket->socket_typeinfo();
840 const eNodeSocketDatatype type = typeinfo ? typeinfo->type : SOCK_CUSTOM;
842 return true;
843 }
844 }
845 return false;
846}
847
848static void draw_property_for_output_socket(DrawGroupInputsContext &ctx,
849 uiLayout *layout,
850 const bNodeTreeInterfaceSocket &socket)
851{
852 const std::string rna_path_attribute_name = fmt::format(
854
855 uiLayout *split = &layout->split(0.4f, false);
856 uiLayout *name_row = &split->row(false);
858 name_row->label(socket.name ? socket.name : "", ICON_NONE);
859
860 uiLayout *row = &split->row(true);
861 add_attribute_search_button(ctx, row, rna_path_attribute_name, socket);
862}
863
864static void draw_output_attributes_panel(DrawGroupInputsContext &ctx, uiLayout *layout)
865{
866 if (!ctx.tree || !ctx.properties) {
867 return;
868 }
869 const Span<const bNodeTreeInterfaceSocket *> interface_outputs = ctx.tree->interface_outputs();
870 for (const int i : interface_outputs.index_range()) {
871 const bNodeTreeInterfaceSocket &socket = *interface_outputs[i];
872 const bke::bNodeSocketType *typeinfo = socket.socket_typeinfo();
873 const eNodeSocketDatatype type = typeinfo ? typeinfo->type : SOCK_CUSTOM;
874 if (!ctx.output_usages[i].is_visible) {
875 continue;
876 }
878 draw_property_for_output_socket(ctx, layout, socket);
879 }
880 }
881}
882
883static void draw_bake_panel(uiLayout *layout, PointerRNA *modifier_ptr)
884{
885 uiLayout *col = &layout->column(false);
886 col->use_property_split_set(true);
887 col->use_property_decorate_set(false);
888 col->prop(modifier_ptr, "bake_target", UI_ITEM_NONE, std::nullopt, ICON_NONE);
889 col->prop(modifier_ptr, "bake_directory", UI_ITEM_NONE, IFACE_("Bake Path"), ICON_NONE);
890}
891
893{
894 if (G.is_rendering) {
895 /* Avoid accessing this data while baking in a separate thread. */
896 return;
897 }
898 geo_log::GeoTreeLog *tree_log = get_root_tree_log(nmd);
899 if (tree_log == nullptr) {
900 return;
901 }
902
904 const Map<StringRefNull, geo_log::NamedAttributeUsage> &usage_by_attribute =
905 tree_log->used_named_attributes;
906
907 if (usage_by_attribute.is_empty()) {
908 layout->label(RPT_("No named attributes used"), ICON_INFO);
909 return;
910 }
911
912 struct NameWithUsage {
915 };
916
917 Vector<NameWithUsage> sorted_used_attribute;
918 for (auto &&item : usage_by_attribute.items()) {
919 sorted_used_attribute.append({item.key, item.value});
920 }
921 std::sort(sorted_used_attribute.begin(),
922 sorted_used_attribute.end(),
923 [](const NameWithUsage &a, const NameWithUsage &b) {
924 return BLI_strcasecmp_natural(a.name.c_str(), b.name.c_str()) < 0;
925 });
926
927 for (const NameWithUsage &attribute : sorted_used_attribute) {
928 const StringRef attribute_name = attribute.name;
929 const geo_log::NamedAttributeUsage usage = attribute.usage;
930
931 /* #uiLayoutRowWithHeading doesn't seem to work in this case. */
932 uiLayout *split = &layout->split(0.4f, false);
933
934 std::stringstream ss;
935 Vector<std::string> usages;
937 usages.append(IFACE_("Read"));
938 }
940 usages.append(IFACE_("Write"));
941 }
944 }
945 for (const int i : usages.index_range()) {
946 ss << usages[i];
947 if (i < usages.size() - 1) {
948 ss << ", ";
949 }
950 }
951
952 uiLayout *row = &split->row(false);
954 row->active_set(false);
955 row->label(ss.str(), ICON_NONE);
956
957 row = &split->row(false);
958 row->label(attribute_name, ICON_NONE);
959 }
960}
961
962static void draw_manage_panel(const bContext *C,
963 uiLayout *layout,
964 PointerRNA *modifier_ptr,
966{
967 if (uiLayout *panel_layout = layout->panel_prop(
968 C, modifier_ptr, "open_bake_panel", IFACE_("Bake")))
969 {
970 draw_bake_panel(panel_layout, modifier_ptr);
971 }
972 if (uiLayout *panel_layout = layout->panel_prop(
973 C, modifier_ptr, "open_named_attributes_panel", IFACE_("Named Attributes")))
974 {
975 draw_named_attributes_panel(panel_layout, nmd);
976 }
977}
978
980{
981 Main *bmain = CTX_data_main(&C);
982 PointerRNA bmain_ptr = RNA_main_pointer_create(bmain);
983 NodesModifierData &nmd = *modifier_ptr->data_as<NodesModifierData>();
984 Object &object = *reinterpret_cast<Object *>(modifier_ptr->owner_id);
985
986 DrawGroupInputsContext ctx{C,
987 nmd.node_group,
990 modifier_ptr,
991 &bmain_ptr};
992
993 ctx.panel_open_property_fn = [&](const bNodeTreeInterfacePanel &io_panel) -> PanelOpenProperty {
994 NodesModifierPanel *panel = find_panel_by_id(nmd, io_panel.identifier);
996 modifier_ptr->owner_id, &RNA_NodesModifierPanel, panel);
997 return {panel_ptr, "is_open"};
998 };
999 ctx.socket_search_data_fn = [&](const bNodeTreeInterfaceSocket &io_socket) -> SocketSearchData {
1000 SocketSearchData data{};
1001 ModifierSearchData &modifier_search_data = data.search_data.emplace<ModifierSearchData>();
1002 modifier_search_data.object_session_uid = object.id.session_uid;
1003 STRNCPY_UTF8(modifier_search_data.modifier_name, nmd.modifier.name);
1004 STRNCPY_UTF8(data.socket_identifier, io_socket.identifier);
1005 data.is_output = io_socket.flag & NODE_INTERFACE_SOCKET_OUTPUT;
1006 return data;
1007 };
1008 ctx.draw_attribute_toggle_fn =
1009 [&](uiLayout &layout, const int icon, const bNodeTreeInterfaceSocket &io_socket) {
1010 PointerRNA props = layout.op("object.geometry_nodes_input_attribute_toggle",
1011 "",
1012 icon,
1014 UI_ITEM_NONE);
1015 RNA_string_set(&props, "modifier_name", nmd.modifier.name);
1016 RNA_string_set(&props, "input_name", io_socket.identifier);
1017 };
1018
1019 layout.use_property_split_set(true);
1020 /* Decorators are added manually for supported properties because the
1021 * attribute/value toggle requires a manually built layout anyway. */
1022 layout.use_property_decorate_set(false);
1023
1025 const char *newop = (nmd.node_group == nullptr) ? "node.new_geometry_node_group_assign" :
1026 "object.geometry_node_tree_copy_assign";
1027 uiTemplateID(&layout, &C, modifier_ptr, "node_group", newop, nullptr, nullptr);
1028 }
1029
1030 if (nmd.node_group != nullptr && nmd.settings.properties != nullptr) {
1031 nmd.runtime->usage_cache.ensure(nmd);
1032 ctx.input_usages = nmd.runtime->usage_cache.inputs;
1033 ctx.output_usages = nmd.runtime->usage_cache.outputs;
1035 }
1036
1037 modifier_error_message_draw(&layout, modifier_ptr);
1038
1039 draw_warnings(&C, nmd, &layout, modifier_ptr);
1040
1042 if (uiLayout *panel_layout = layout.panel_prop(
1043 &C, modifier_ptr, "open_output_attributes_panel", IFACE_("Output Attributes")))
1044 {
1045 draw_output_attributes_panel(ctx, panel_layout);
1046 }
1047 }
1048
1049 if ((nmd.flag & NODES_MODIFIER_HIDE_MANAGE_PANEL) == 0) {
1050 if (uiLayout *panel_layout = layout.panel_prop(
1051 &C, modifier_ptr, "open_manage_panel", IFACE_("Manage")))
1052 {
1053 draw_manage_panel(&C, panel_layout, modifier_ptr, nmd);
1054 }
1055 }
1056}
1057
1059 wmOperator &op,
1060 bNodeTree &tree,
1061 geo_eval_log::GeoTreeLog *tree_log)
1062{
1063 uiLayout &layout = *op.layout;
1064 Main &bmain = *CTX_data_main(&C);
1065 PointerRNA bmain_ptr = RNA_main_pointer_create(&bmain);
1066
1067 DrawGroupInputsContext ctx{C, &tree, tree_log, op.properties, op.ptr, &bmain_ptr};
1068 ctx.panel_open_property_fn = [&](const bNodeTreeInterfacePanel &io_panel) -> PanelOpenProperty {
1069 Panel *root_panel = layout.root_panel();
1071 root_panel,
1072 "node_operator_panel_" + std::to_string(io_panel.identifier),
1074 PointerRNA state_ptr = RNA_pointer_create_discrete(nullptr, &RNA_LayoutPanelState, state);
1075 return {state_ptr, "is_open"};
1076 };
1077 ctx.socket_search_data_fn = [&](const bNodeTreeInterfaceSocket &io_socket) -> SocketSearchData {
1078 SocketSearchData data{};
1079 OperatorSearchData &operator_search_data = data.search_data.emplace<OperatorSearchData>();
1080 operator_search_data.info.tree = &tree;
1081 operator_search_data.info.tree_log = tree_log;
1082 operator_search_data.info.properties = op.properties;
1083 STRNCPY_UTF8(data.socket_identifier, io_socket.identifier);
1084 data.is_output = io_socket.flag & NODE_INTERFACE_SOCKET_OUTPUT;
1085 return data;
1086 };
1087 ctx.draw_attribute_toggle_fn =
1088 [&](uiLayout &layout, const int icon, const bNodeTreeInterfaceSocket &io_socket) {
1089 const std::string prop_name = fmt::format(
1090 "[\"{}{}\"]", BLI_str_escape(io_socket.identifier), nodes::input_use_attribute_suffix);
1091 layout.prop(op.ptr, prop_name, UI_ITEM_R_ICON_ONLY, "", icon);
1092 };
1093 ctx.use_name_for_ids = true;
1094
1095 layout.use_property_split_set(true);
1096 /* Decorators are added manually for supported properties because the
1097 * attribute/value toggle requires a manually built layout anyway. */
1098 layout.use_property_decorate_set(false);
1099
1100 tree.ensure_interface_cache();
1101 ctx.input_usages.reinitialize(tree.interface_inputs().size());
1102 ctx.output_usages.reinitialize(tree.interface_outputs().size());
1104 tree, ctx.properties, ctx.input_usages, ctx.output_usages);
1105 draw_interface_panel_content(ctx, &layout, tree.tree_interface.root_panel);
1106}
1107
1108} // 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:747
void IDP_AssignString(IDProperty *prop, const char *st) ATTR_NONNULL()
Definition idprop.cc:439
IDProperty * IDP_GetPropertyFromGroup_null(const IDProperty *prop, blender::StringRef name) ATTR_WARN_UNUSED_RESULT
Definition idprop.cc:760
ID * BKE_libblock_find_session_uid(Main *bmain, short type, uint32_t session_uid)
Definition lib_id.cc:1728
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:520
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:83
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ATTR_FALLTHROUGH
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 STRNCPY_UTF8(dst, src)
#define RPT_(msgid)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
struct IDProperty IDProperty
@ ID_OB
#define MAX_NAME
Definition DNA_defs.h:50
@ NODES_MODIFIER_HIDE_DATABLOCK_SELECTOR
@ NODES_MODIFIER_HIDE_MANAGE_PANEL
@ 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:98
static void split(const char *text, const char *seps, char ***str, int *count)
void modifier_error_message_draw(uiLayout *layout, PointerRNA *ptr)
@ PROP_POINTER
Definition RNA_types.hh:167
#define C
Definition RandGen.cpp:29
uiBut * uiDefIconTextBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, std::optional< blender::StringRef > tip)
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)
@ UI_BUT_REDALERT
void UI_but_func_tooltip_set(uiBut *but, uiButToolTipFunc func, void *arg, uiFreeArgFunc free_arg)
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
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)
uiBut * uiDefIconTextButR(uiBlock *block, ButType 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, std::optional< blender::StringRef > tip)
#define UI_UNIT_X
void UI_but_flag_enable(uiBut *but, int flag)
@ UI_ITEM_R_EXPAND
@ UI_ITEM_R_ICON_ONLY
#define UI_ITEM_NONE
void uiLayoutSetTooltipFunc(uiLayout *layout, uiButToolTipFunc func, void *arg, uiCopyArgFunc copy_arg, uiFreeArgFunc free_arg)
BMesh const char void * data
const ComputeContextHash & hash() const
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
bool is_empty() const
Definition BLI_map.hh:986
ItemIterator items() const &
Definition BLI_map.hh:902
Value & lookup_or_add(const Key &key, const Value &value)
Definition BLI_map.hh:588
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr int64_t size() 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
static const char * modifier_name[LS_MODIFIER_NUM]
Definition linestyle.cc:689
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
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_usage(const bNodeTree &group, const Span< InferenceValue > group_input_values, const MutableSpan< SocketUsage > r_input_usages, const std::optional< MutableSpan< SocketUsage > > r_output_usages)
std::optional< StringRef > input_attribute_name_get(const IDProperty *properties, const bNodeTreeInterfaceSocket &io_input)
static void draw_output_attributes_panel(DrawGroupInputsContext &ctx, uiLayout *layout)
bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index)
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)
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_interface_panel_as_panel(DrawGroupInputsContext &ctx, uiLayout &layout, const bNodeTreeInterfacePanel &interface_panel)
static std::string get_node_warning_panel_name(const int num_errors, const int num_warnings, const int num_infos)
static void draw_named_attributes_panel(uiLayout *layout, NodesModifierData &nmd)
static void draw_interface_panel_content(DrawGroupInputsContext &ctx, uiLayout *layout, const bNodeTreeInterfacePanel &interface_panel, const bool skip_first=false, const std::optional< StringRef > parent_name=std::nullopt)
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)
static void draw_property_for_socket(DrawGroupInputsContext &ctx, uiLayout *layout, const bNodeTreeInterfaceSocket &socket, const std::optional< StringRef > parent_name=std::nullopt)
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
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)
const char * name
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
PropertyType RNA_property_type(PropertyRNA *prop)
std::string RNA_string_get(PointerRNA *ptr, const char *name)
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:164
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:158
eNodeSocketDatatype type
Definition BKE_node.hh:193
void use_property_decorate_set(bool is_sep)
void alignment_set(blender::ui::LayoutAlign alignment)
void decorator(PointerRNA *ptr, PropertyRNA *prop, int index)
PanelLayout panel_prop(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name)
uiBlock * block() const
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::StringRef > label)
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
void prop_search(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop, PropertyRNA *item_searchpropname, std::optional< blender::StringRefNull > name, int icon, bool results_are_suggestions)
void active_set(bool active)
uiLayout & row(bool align)
uiLayout & split(float percentage, bool align)
Panel * root_panel() const
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
void use_property_split_set(bool value)
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:4238