Blender V4.3
node_templates.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
9#include <cstdlib>
10#include <cstring>
11#include <optional>
12
13#include "MEM_guardedalloc.h"
14
15#include "DNA_node_types.h"
16#include "DNA_screen_types.h"
17
18#include "BLI_listbase.h"
19#include "BLI_string.h"
20#include "BLI_string_utf8.h"
21#include "BLI_vector.hh"
22
23#include "BLT_translation.hh"
24
25#include "BKE_context.hh"
26#include "BKE_lib_id.hh"
27#include "BKE_main.hh"
28#include "BKE_node_runtime.hh"
31
32#include "RNA_access.hh"
33#include "RNA_prototypes.hh"
34
36#include "NOD_socket.hh"
38
39#include "../interface/interface_intern.hh" /* XXX bad level */
40#include "UI_interface.hh"
41
42#include "ED_node.hh" /* own include */
43#include "node_intern.hh"
44
45#include "ED_undo.hh"
46
47#include "WM_api.hh"
48
50
52
53/************************* Node Socket Manipulation **************************/
54
55/* describes an instance of a node type and a specific socket to link */
57 int socket_index; /* index for linking */
58 int socket_type; /* socket type for compatibility check */
59 const char *socket_name; /* ui label of the socket */
60 const char *node_name; /* ui label of the node */
61
62 /* extra settings */
63 bNodeTree *ngroup; /* group node tree */
64};
65
67{
68 item.socket_index = -1;
70 item.socket_name = nullptr;
71 item.node_name = nullptr;
72 item.ngroup = nullptr;
73}
74
75/* Compare an existing node to a link item to see if it can be reused.
76 * item must be for the same node type!
77 * XXX should become a node type callback
78 */
79static bool node_link_item_compare(bNode *node, NodeLinkItem *item)
80{
81 if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
82 return (node->id == (ID *)item->ngroup);
83 }
84 return true;
85}
86
87static void node_link_item_apply(bNodeTree *ntree, bNode *node, NodeLinkItem *item)
88{
89 if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
90 node->id = (ID *)item->ngroup;
92 }
93 else {
94 /* nothing to do for now */
95 }
96
97 if (node->id) {
98 id_us_plus(node->id);
99 }
100}
101
102static void node_tag_recursive(bNode *node)
103{
104 if (!node || (node->flag & NODE_TEST)) {
105 return; /* in case of cycles */
106 }
107
108 node->flag |= NODE_TEST;
109
110 LISTBASE_FOREACH (bNodeSocket *, input, &node->inputs) {
111 if (input->link) {
112 node_tag_recursive(input->link->fromnode);
113 }
114 }
115}
116
117static void node_clear_recursive(bNode *node)
118{
119 if (!node || !(node->flag & NODE_TEST)) {
120 return; /* in case of cycles */
121 }
122
123 node->flag &= ~NODE_TEST;
124
125 LISTBASE_FOREACH (bNodeSocket *, input, &node->inputs) {
126 if (input->link) {
127 node_clear_recursive(input->link->fromnode);
128 }
129 }
130}
131
132static void node_remove_linked(Main *bmain, bNodeTree *ntree, bNode *rem_node)
133{
134 bNode *node, *next;
135
136 if (!rem_node) {
137 return;
138 }
139
140 /* tag linked nodes to be removed */
141 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
142 node->flag &= ~NODE_TEST;
143 }
144
145 node_tag_recursive(rem_node);
146
147 /* clear tags on nodes that are still used by other nodes */
148 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
149 if (!(node->flag & NODE_TEST)) {
150 LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
151 if (sock->link && sock->link->fromnode != rem_node) {
152 node_clear_recursive(sock->link->fromnode);
153 }
154 }
155 }
156 }
157
158 /* remove nodes */
159 for (node = (bNode *)ntree->nodes.first; node; node = next) {
160 next = node->next;
161
162 if (node->flag & NODE_TEST) {
163 bke::node_remove_node(bmain, ntree, node, true);
164 }
165 }
166}
167
168/* disconnect socket from the node it is connected to */
169static void node_socket_disconnect(Main *bmain,
170 bNodeTree *ntree,
171 bNode *node_to,
172 bNodeSocket *sock_to)
173{
174 if (!sock_to->link) {
175 return;
176 }
177
178 bke::node_remove_link(ntree, sock_to->link);
179 sock_to->flag |= SOCK_COLLAPSED;
180
182 ED_node_tree_propagate_change(nullptr, bmain, ntree);
183}
184
185/* remove all nodes connected to this socket, if they aren't connected to other nodes */
186static void node_socket_remove(Main *bmain, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to)
187{
188 if (!sock_to->link) {
189 return;
190 }
191
192 node_remove_linked(bmain, ntree, sock_to->link->fromnode);
193 sock_to->flag |= SOCK_COLLAPSED;
194
196 ED_node_tree_propagate_change(nullptr, bmain, ntree);
197}
198
199/* add new node connected to this socket, or replace an existing one */
201 bNodeTree *ntree,
202 bNode *node_to,
203 bNodeSocket *sock_to,
204 int type,
205 NodeLinkItem *item)
206{
207 Main *bmain = CTX_data_main(C);
208 bNode *node_from;
209 bNodeSocket *sock_from_tmp;
210 bNode *node_prev = nullptr;
211
212 /* unlink existing node */
213 if (sock_to->link) {
214 node_prev = sock_to->link->fromnode;
215 bke::node_remove_link(ntree, sock_to->link);
216 }
217
218 /* find existing node that we can use */
219 for (node_from = (bNode *)ntree->nodes.first; node_from; node_from = node_from->next) {
220 if (node_from->type == type) {
221 break;
222 }
223 }
224
225 if (node_from) {
226 if (node_from->inputs.first || node_from->typeinfo->draw_buttons ||
227 node_from->typeinfo->draw_buttons_ex)
228 {
229 node_from = nullptr;
230 }
231 }
232
233 if (node_prev && node_prev->type == type && node_link_item_compare(node_prev, item)) {
234 /* keep the previous node if it's the same type */
235 node_from = node_prev;
236 }
237 else if (!node_from) {
238 node_from = bke::node_add_static_node(C, ntree, type);
239 if (node_prev != nullptr) {
240 /* If we're replacing existing node, use its location. */
241 node_from->locx = node_prev->locx;
242 node_from->locy = node_prev->locy;
243 node_from->offsetx = node_prev->offsetx;
244 node_from->offsety = node_prev->offsety;
245 }
246 else {
247 sock_from_tmp = (bNodeSocket *)BLI_findlink(&node_from->outputs, item->socket_index);
248 bke::node_position_relative(node_from, node_to, sock_from_tmp, sock_to);
249 }
250
251 node_link_item_apply(ntree, node_from, item);
252 ED_node_tree_propagate_change(C, bmain, ntree);
253 }
254
255 bke::node_set_active(ntree, node_from);
256
257 /* add link */
258 sock_from_tmp = (bNodeSocket *)BLI_findlink(&node_from->outputs, item->socket_index);
259 bke::node_add_link(ntree, node_from, sock_from_tmp, node_to, sock_to);
260 sock_to->flag &= ~SOCK_COLLAPSED;
261
262 /* copy input sockets from previous node */
263 if (node_prev && node_from != node_prev) {
264 LISTBASE_FOREACH (bNodeSocket *, sock_prev, &node_prev->inputs) {
265 LISTBASE_FOREACH (bNodeSocket *, sock_from, &node_from->inputs) {
266 if (bke::node_count_socket_links(ntree, sock_from) >=
268 {
269 continue;
270 }
271
272 if (STREQ(sock_prev->identifier, sock_from->identifier) &&
273 sock_prev->type == sock_from->type)
274 {
275 bNodeLink *link = sock_prev->link;
276
277 if (link && link->fromnode) {
278 bke::node_add_link(ntree, link->fromnode, link->fromsock, node_from, sock_from);
279 bke::node_remove_link(ntree, link);
280 }
281
282 node_socket_copy_default_value(sock_from, sock_prev);
283 }
284 }
285 }
286
287 /* also preserve mapping for texture nodes */
288 if (node_from->typeinfo->nclass == NODE_CLASS_TEXTURE &&
289 node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE &&
290 /* White noise texture node does not have NodeTexBase. */
291 node_from->storage != nullptr && node_prev->storage != nullptr)
292 {
293 memcpy(node_from->storage, node_prev->storage, sizeof(NodeTexBase));
294 }
295
296 /* remove node */
297 node_remove_linked(bmain, ntree, node_prev);
298 }
299
300 BKE_ntree_update_tag_node_property(ntree, node_from);
302 ED_node_tree_propagate_change(nullptr, bmain, ntree);
303}
304
305/****************************** Node Link Menu *******************************/
306
307// #define UI_NODE_LINK_ADD 0
308#define UI_NODE_LINK_DISCONNECT -1
309#define UI_NODE_LINK_REMOVE -2
310
323
325 int in_out,
326 std::optional<NodeDeclaration> &r_node_decl)
327{
329
330 /* XXX this should become a callback for node types! */
331 if (arg->node_type->type == NODE_GROUP) {
332 bNodeTree *ngroup;
333
334 for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup;
335 ngroup = (bNodeTree *)ngroup->id.next)
336 {
337 const char *disabled_hint;
338 if ((ngroup->type != arg->ntree->type) ||
339 !bke::node_group_poll(arg->ntree, ngroup, &disabled_hint))
340 {
341 continue;
342 }
343 }
344
345 for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup;
346 ngroup = (bNodeTree *)ngroup->id.next)
347 {
348 const char *disabled_hint;
349 if ((ngroup->type != arg->ntree->type) ||
350 !bke::node_group_poll(arg->ntree, ngroup, &disabled_hint))
351 {
352 continue;
353 }
354
355 ngroup->ensure_interface_cache();
356 Span<bNodeTreeInterfaceSocket *> iosockets = (in_out == SOCK_IN ?
357 ngroup->interface_inputs() :
358 ngroup->interface_outputs());
359 for (const int index : iosockets.index_range()) {
360 bNodeTreeInterfaceSocket *iosock = iosockets[index];
361 NodeLinkItem item;
363 item.socket_index = index;
364 /* NOTE: int stemp->type is not fully reliable, not used for node group
365 * interface sockets. use the typeinfo->type instead.
366 */
367 const bke::bNodeSocketType *typeinfo = iosock->socket_typeinfo();
368 item.socket_type = typeinfo->type;
369 item.socket_name = iosock->name;
370 item.node_name = ngroup->id.name + 2;
371 item.ngroup = ngroup;
372
373 items.append(item);
374 }
375 }
376 }
377 else if (arg->node_type->declare != nullptr) {
378 using namespace blender;
379 using namespace blender::nodes;
380
381 r_node_decl.emplace(NodeDeclaration());
382 blender::nodes::build_node_declaration(*arg->node_type, *r_node_decl, nullptr, nullptr);
383 Span<SocketDeclaration *> socket_decls = (in_out == SOCK_IN) ? r_node_decl->inputs :
384 r_node_decl->outputs;
385 int index = 0;
386 for (const SocketDeclaration *socket_decl_ptr : socket_decls) {
387 const SocketDeclaration &socket_decl = *socket_decl_ptr;
388 NodeLinkItem item;
390 item.socket_index = index++;
391 item.socket_type = socket_decl.socket_type;
392 item.socket_name = socket_decl.name.c_str();
393 item.node_name = arg->node_type->ui_name;
394 items.append(item);
395 }
396 }
397 else {
398 bke::bNodeSocketTemplate *socket_templates = (in_out == SOCK_IN ? arg->node_type->inputs :
399 arg->node_type->outputs);
401 int i;
402
403 i = 0;
404 for (stemp = socket_templates; stemp && stemp->type != -1; stemp++, i++) {
405 NodeLinkItem item;
407 item.socket_index = i;
408 item.socket_type = stemp->type;
409 item.socket_name = stemp->name;
410 item.node_name = arg->node_type->ui_name;
411 items.append(item);
412 }
413 }
414
415 return items;
416}
417
418static void ui_node_link(bContext *C, void *arg_p, void *event_p)
419{
420 NodeLinkArg *arg = (NodeLinkArg *)arg_p;
421 Main *bmain = arg->bmain;
422 bNode *node_to = arg->node;
423 bNodeSocket *sock_to = arg->sock;
424 bNodeTree *ntree = arg->ntree;
425 int event = POINTER_AS_INT(event_p);
426
427 if (event == UI_NODE_LINK_DISCONNECT) {
428 node_socket_disconnect(bmain, ntree, node_to, sock_to);
429 }
430 else if (event == UI_NODE_LINK_REMOVE) {
431 node_socket_remove(bmain, ntree, node_to, sock_to);
432 }
433 else {
434 node_socket_add_replace(C, ntree, node_to, sock_to, arg->node_type->type, &arg->item);
435 }
436
437 ED_undo_push(C, "Node input modify");
438}
439
440static void ui_node_sock_name(const bNodeTree *ntree,
441 bNodeSocket *sock,
442 char name[UI_MAX_NAME_STR])
443{
444 if (sock->link && sock->link->fromnode) {
445 bNode *node = sock->link->fromnode;
446 char node_name[UI_MAX_NAME_STR];
447
448 bke::nodeLabel(ntree, node, node_name, sizeof(node_name));
449
450 if (BLI_listbase_is_empty(&node->inputs) && node->outputs.first != node->outputs.last) {
452 name, UI_MAX_NAME_STR, "%s | %s", IFACE_(node_name), IFACE_(sock->link->fromsock->name));
453 }
454 else {
455 BLI_strncpy_utf8(name, IFACE_(node_name), UI_MAX_NAME_STR);
456 }
457 }
458 else if (sock->type == SOCK_SHADER) {
460 }
461 else {
462 BLI_strncpy_utf8(name, IFACE_("Default"), UI_MAX_NAME_STR);
463 }
464}
465
466static int ui_compatible_sockets(int typeA, int typeB)
467{
468 return (typeA == typeB);
469}
470
471static int ui_node_item_name_compare(const void *a, const void *b)
472{
473 const bke::bNodeType *type_a = *(const bke::bNodeType **)a;
474 const bke::bNodeType *type_b = *(const bke::bNodeType **)b;
475 return BLI_strcasecmp_natural(type_a->ui_name, type_b->ui_name);
476}
477
478static bool ui_node_item_special_poll(const bNodeTree * /*ntree*/, const bke::bNodeType *ntype)
479{
480 if (STREQ(ntype->idname, "ShaderNodeUVAlongStroke")) {
481 /* TODO(sergey): Currently we don't have Freestyle nodes edited from
482 * the buttons context, so can ignore its nodes completely.
483 *
484 * However, we might want to do some extra checks here later.
485 */
486 return false;
487 }
488 return true;
489}
490
491static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
492{
493 bNodeTree *ntree = arg->ntree;
494 bNodeSocket *sock = arg->sock;
495 uiLayout *layout = arg->layout;
496 uiLayout *column = nullptr;
497 uiBlock *block = uiLayoutGetBlock(layout);
498 uiBut *but;
499 NodeLinkArg *argN;
500 int first = 1;
501
502 /* generate array of node types sorted by UI name */
504
505 NODE_TYPES_BEGIN (ntype) {
506 const char *disabled_hint;
507 if (!(ntype->poll && ntype->poll(ntype, ntree, &disabled_hint))) {
508 continue;
509 }
510
511 if (ntype->nclass != nclass) {
512 continue;
513 }
514
515 if (!ui_node_item_special_poll(ntree, ntype)) {
516 continue;
517 }
518
519 sorted_ntypes.append(ntype);
520 }
522
523 qsort(sorted_ntypes.data(),
524 sorted_ntypes.size(),
525 sizeof(bke::bNodeType *),
527
528 /* generate UI */
529 for (int j = 0; j < sorted_ntypes.size(); j++) {
530 bke::bNodeType *ntype = sorted_ntypes[j];
531 char name[UI_MAX_NAME_STR];
532 const char *cur_node_name = nullptr;
533 int num = 0;
534 int icon = ICON_NONE;
535
536 arg->node_type = ntype;
537
538 std::optional<blender::nodes::NodeDeclaration> node_decl;
539 Vector<NodeLinkItem> items = ui_node_link_items(arg, SOCK_OUT, node_decl);
540
541 for (const NodeLinkItem &item : items) {
542 if (ui_compatible_sockets(item.socket_type, sock->type)) {
543 num++;
544 }
545 }
546
547 for (const NodeLinkItem &item : items) {
548 if (!ui_compatible_sockets(item.socket_type, sock->type)) {
549 continue;
550 }
551
552 if (first) {
553 column = uiLayoutColumn(layout, false);
554 UI_block_layout_set_current(block, column);
555
556 uiItemL(column, IFACE_(cname), ICON_NODE);
557 but = (uiBut *)block->buttons.last;
558
559 first = 0;
560 }
561
562 if (num > 1) {
563 if (!cur_node_name || !STREQ(cur_node_name, item.node_name)) {
564 cur_node_name = item.node_name;
565 /* XXX Do not use uiItemL here,
566 * it would add an empty icon as we are in a menu! */
567 uiDefBut(block,
569 0,
570 IFACE_(cur_node_name),
571 0,
572 0,
573 UI_UNIT_X * 4,
574 UI_UNIT_Y,
575 nullptr,
576 0.0,
577 0.0,
578 "");
579 }
580
581 SNPRINTF(name, "%s", IFACE_(item.socket_name));
582 icon = ICON_BLANK1;
583 }
584 else {
585 STRNCPY_UTF8(name, IFACE_(item.node_name));
586 icon = ICON_NONE;
587 }
588
589 but = uiDefIconTextBut(block,
591 0,
592 icon,
593 name,
594 0,
595 0,
596 UI_UNIT_X * 4,
597 UI_UNIT_Y,
598 nullptr,
599 0.0,
600 0.0,
601 TIP_("Add node to input"));
602
603 argN = (NodeLinkArg *)MEM_dupallocN(arg);
604 argN->item = item;
605 UI_but_funcN_set(but, ui_node_link, argN, nullptr);
606 }
607 }
608}
609
610static void node_menu_column_foreach_cb(void *calldata, int nclass, const char *name)
611{
612 NodeLinkArg *arg = (NodeLinkArg *)calldata;
613
614 if (!ELEM(nclass, NODE_CLASS_GROUP, NODE_CLASS_LAYOUT)) {
615 ui_node_menu_column(arg, nclass, name);
616 }
617}
618
619static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_p)
620{
621 Main *bmain = CTX_data_main(C);
622 Scene *scene = CTX_data_scene(C);
623 uiBlock *block = uiLayoutGetBlock(layout);
624 uiBut *but = (uiBut *)but_p;
625 uiLayout *split, *column;
626 NodeLinkArg *arg = (NodeLinkArg *)but->func_argN;
627 bNodeSocket *sock = arg->sock;
628 bke::bNodeTreeType *ntreetype = arg->ntree->typeinfo;
629
630 UI_block_layout_set_current(block, layout);
631 split = uiLayoutSplit(layout, 0.0f, false);
632
633 arg->bmain = bmain;
634 arg->scene = scene;
635 arg->layout = split;
636
637 if (ntreetype && ntreetype->foreach_nodeclass) {
639 }
640
641 column = uiLayoutColumn(split, false);
642 UI_block_layout_set_current(block, column);
643
644 if (sock->link) {
645 uiItemL(column, IFACE_("Link"), ICON_NONE);
646 but = (uiBut *)block->buttons.last;
648
649 but = uiDefBut(block,
651 0,
653 0,
654 0,
655 UI_UNIT_X * 4,
656 UI_UNIT_Y,
657 nullptr,
658 0.0,
659 0.0,
660 TIP_("Remove nodes connected to the input"));
662
663 but = uiDefBut(block,
665 0,
666 IFACE_("Disconnect"),
667 0,
668 0,
669 UI_UNIT_X * 4,
670 UI_UNIT_Y,
671 nullptr,
672 0.0,
673 0.0,
674 TIP_("Disconnect nodes connected to the input"));
677 }
678
680}
681
682} // namespace blender::ed::space_node
683
685 uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
686{
687 using namespace blender::ed::space_node;
688
689 uiBlock *block = uiLayoutGetBlock(layout);
690 NodeLinkArg *arg;
691 uiBut *but;
692 float socket_col[4];
693
694 arg = MEM_cnew<NodeLinkArg>("NodeLinkArg");
695 arg->ntree = ntree;
696 arg->node = node;
697 arg->sock = input;
698 node_link_item_init(arg->item);
699
700 PointerRNA node_ptr = RNA_pointer_create(&ntree->id, &RNA_Node, node);
701 node_socket_color_get(*C, *ntree, node_ptr, *input, socket_col);
702
703 UI_block_layout_set_current(block, layout);
704
705 if (input->link || input->type == SOCK_SHADER || (input->flag & SOCK_HIDE_VALUE)) {
706 char name[UI_MAX_NAME_STR];
707 ui_node_sock_name(ntree, input, name);
708 but = uiDefMenuBut(
709 block, ui_template_node_link_menu, nullptr, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
710 }
711 else {
712 but = uiDefIconMenuBut(
713 block, ui_template_node_link_menu, nullptr, ICON_NONE, 0, 0, UI_UNIT_X, UI_UNIT_Y, "");
714 }
715
717 UI_but_node_link_set(but, input, socket_col);
719
720 but->poin = (char *)but;
721 but->func_argN = arg;
724
725 if (input->link && input->link->fromnode) {
726 if (input->link->fromnode->flag & NODE_ACTIVE_TEXTURE) {
727 but->flag |= UI_BUT_NODE_ACTIVE;
728 }
729 }
730}
731
732namespace blender::ed::space_node {
733
734/**************************** Node Tree Layout *******************************/
735
736static void ui_node_draw_input(uiLayout &layout,
737 bContext &C,
738 bNodeTree &ntree,
739 bNode &node,
740 bNodeSocket &input,
741 int depth,
742 const char *panel_label);
743
744static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, void *ntree_argv)
745{
746 Main *bmain = CTX_data_main(C);
747 bNodePanelState *panel_state = static_cast<bNodePanelState *>(panel_state_argv);
748 bNodeTree *ntree = static_cast<bNodeTree *>(ntree_argv);
749
750 panel_state->flag ^= NODE_PANEL_COLLAPSED;
751
752 ED_node_tree_propagate_change(C, bmain, ntree);
753
754 /* Make sure panel state updates from the Properties Editor, too. */
756}
757
758static void ui_node_draw_panel(uiLayout &layout,
759 bContext &C,
760 bNodeTree &ntree,
761 const nodes::PanelDeclaration &panel_decl,
762 bNodePanelState &panel_state,
763 PointerRNA nodeptr)
764{
765 uiLayout *row = uiLayoutRow(&layout, true);
766 uiLayoutSetPropDecorate(row, false);
767
768 /* Panel header with collapse icon */
769 uiBlock *block = uiLayoutGetBlock(row);
771 uiBut *but = uiDefIconTextBut(block,
773 0,
774 panel_state.is_collapsed() ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT,
775 IFACE_(panel_decl.name.c_str()),
776 0,
777 0,
778 UI_UNIT_X * 4,
779 UI_UNIT_Y,
780 nullptr,
781 0.0,
782 0.0,
783 "");
785 UI_but_func_set(but, node_panel_toggle_button_cb, &panel_state, &ntree);
787
788 /* Panel buttons. */
789 if (!panel_state.is_collapsed() && panel_decl.draw_buttons) {
790 uiLayoutSetPropSep(&layout, true);
791 panel_decl.draw_buttons(&layout, &C, &nodeptr);
792 }
793}
794
796 uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, int depth)
797{
798 PointerRNA nodeptr = RNA_pointer_create(&ntree.id, &RNA_Node, &node);
799
800 if (node.typeinfo->draw_buttons) {
801 if (node.type != NODE_GROUP) {
802 uiLayoutSetPropSep(&layout, true);
803 node.typeinfo->draw_buttons(&layout, &C, &nodeptr);
804 }
805 }
806
807 if (node.declaration() && node.declaration()->use_custom_socket_order) {
808 /* Node with panels. */
809 namespace nodes = blender::nodes;
811 using SocketIterator = blender::Span<bNodeSocket *>::iterator;
812 using PanelStateIterator = blender::MutableSpan<bNodePanelState>::iterator;
813
814 ItemDeclIterator item_decl = node.declaration()->items.begin();
815 SocketIterator input = node.input_sockets().begin();
816 PanelStateIterator panel_state = node.panel_states().begin();
817 const ItemDeclIterator item_decl_end = node.declaration()->items.end();
818
819 bool panel_collapsed = false;
820 const char *panel_label = nullptr;
821
822 for (; item_decl != item_decl_end; ++item_decl) {
823 if (const nodes::SocketDeclaration *socket_decl =
824 dynamic_cast<const nodes::SocketDeclaration *>(item_decl->get()))
825 {
826 if (socket_decl->in_out == SOCK_IN) {
827 if (!panel_collapsed) {
828 ui_node_draw_input(layout, C, ntree, node, **input, depth + 1, panel_label);
829 }
830 ++input;
831 }
832 }
833 else if (const nodes::PanelDeclaration *panel_decl =
834 dynamic_cast<const nodes::PanelDeclaration *>(item_decl->get()))
835 {
836 panel_collapsed = panel_state->is_collapsed();
837 panel_label = panel_decl->name.c_str();
838 ui_node_draw_panel(layout, C, ntree, *panel_decl, *panel_state, nodeptr);
839 ++panel_state;
840 }
841 }
842 }
843 else {
844 /* Node without panels. */
845 LISTBASE_FOREACH (bNodeSocket *, input, &node.inputs) {
846 ui_node_draw_input(layout, C, ntree, node, *input, depth + 1, nullptr);
847 }
848 }
849}
850
851static void ui_node_draw_input(uiLayout &layout,
852 bContext &C,
853 bNodeTree &ntree,
854 bNode &node,
855 bNodeSocket &input,
856 int depth,
857 const char *panel_label)
858{
859 uiBlock *block = uiLayoutGetBlock(&layout);
860 uiLayout *row = nullptr;
861 bool dependency_loop;
862
863 if (input.flag & SOCK_UNAVAIL) {
864 return;
865 }
866
867 /* to avoid eternal loops on cyclic dependencies */
868 node.flag |= NODE_TEST;
869 bNode *lnode = (input.link) ? input.link->fromnode : nullptr;
870
871 dependency_loop = (lnode && (lnode->flag & NODE_TEST));
872 if (dependency_loop) {
873 lnode = nullptr;
874 }
875
876 /* socket RNA pointer */
877 PointerRNA inputptr = RNA_pointer_create(&ntree.id, &RNA_NodeSocket, &input);
878 PointerRNA nodeptr = RNA_pointer_create(&ntree.id, &RNA_Node, &node);
879
880 row = uiLayoutRow(&layout, true);
881
883 /* Decorations are added manually here. */
884 uiLayoutSetPropDecorate(row, false);
885 /* Empty decorator item for alignment. */
886 bool add_dummy_decorator = false;
887
888 {
889 uiLayout *sub = uiLayoutRow(split_wrapper.label_column, true);
890
891 if (depth > 0) {
893
894 if (lnode &&
895 (lnode->inputs.first || (lnode->typeinfo->draw_buttons && lnode->type != NODE_GROUP)))
896 {
897 int icon = (input.flag & SOCK_COLLAPSED) ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT;
898 uiItemR(sub, &inputptr, "show_expanded", UI_ITEM_R_ICON_ONLY, "", icon);
899 }
900
902 }
903
904 sub = uiLayoutRow(sub, true);
906 uiItemL(sub, node_socket_get_label(&input, panel_label), ICON_NONE);
907 }
908
909 if (dependency_loop) {
910 uiItemL(row, RPT_("Dependency Loop"), ICON_ERROR);
911 add_dummy_decorator = true;
912 }
913 else if (lnode) {
914 /* input linked to a node */
915 uiTemplateNodeLink(row, &C, &ntree, &node, &input);
916 add_dummy_decorator = true;
917
918 if (depth == 0 || !(input.flag & SOCK_COLLAPSED)) {
919 if (depth == 0) {
920 uiItemS(&layout);
921 }
922
923 ui_node_draw_node(layout, C, ntree, *lnode, depth);
924 }
925 }
926 else {
927 uiLayout *sub = uiLayoutRow(row, true);
928
929 uiTemplateNodeLink(sub, &C, &ntree, &node, &input);
930
931 if (input.flag & SOCK_HIDE_VALUE) {
932 add_dummy_decorator = true;
933 }
934 /* input not linked, show value */
935 else {
936 switch (input.type) {
937 case SOCK_VECTOR:
938 uiItemS(sub);
939 sub = uiLayoutColumn(sub, true);
941 case SOCK_FLOAT:
942 case SOCK_INT:
943 case SOCK_ROTATION:
944 case SOCK_BOOLEAN:
945 case SOCK_RGBA:
946 uiItemR(sub, &inputptr, "default_value", UI_ITEM_NONE, "", ICON_NONE);
947 if (split_wrapper.decorate_column) {
949 split_wrapper.decorate_column, &inputptr, "default_value", RNA_NO_INDEX);
950 }
951 break;
952 case SOCK_STRING: {
953 const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id;
954 SpaceNode *snode = CTX_wm_space_node(&C);
955 if (node_tree->type == NTREE_GEOMETRY && snode != nullptr) {
956 /* Only add the attribute search in the node editor, in other places there is not
957 * enough context. */
958 node_geometry_add_attribute_search_button(C, node, inputptr, *sub);
959 }
960 else {
961 uiItemR(sub, &inputptr, "default_value", UI_ITEM_NONE, "", ICON_NONE);
962 }
963 if (split_wrapper.decorate_column) {
965 split_wrapper.decorate_column, &inputptr, "default_value", RNA_NO_INDEX);
966 }
967 break;
968 }
969 case SOCK_MENU:
970 uiItemL(sub, RPT_("Unsupported Menu Socket"), ICON_NONE);
971 break;
972 case SOCK_CUSTOM:
973 input.typeinfo->draw(&C, sub, &inputptr, &nodeptr, input.name);
974 break;
975 default:
976 add_dummy_decorator = true;
977 }
978 }
979 }
980
981 if (add_dummy_decorator && split_wrapper.decorate_column) {
982 uiItemDecoratorR(split_wrapper.decorate_column, nullptr, nullptr, 0);
983 }
984
985 node_socket_add_tooltip(ntree, input, *row);
986
987 /* clear */
988 node.flag &= ~NODE_TEST;
989}
990
991} // namespace blender::ed::space_node
992
994 uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
995{
996 using namespace blender::ed::space_node;
997
998 if (!ntree) {
999 return;
1000 }
1001 ntree->ensure_topology_cache();
1002
1003 /* clear for cycle check */
1004 LISTBASE_FOREACH (bNode *, tnode, &ntree->nodes) {
1005 tnode->flag &= ~NODE_TEST;
1006 }
1007
1008 if (input) {
1009 ui_node_draw_input(*layout, *C, *ntree, *node, *input, 0, nullptr);
1010 }
1011 else {
1012 ui_node_draw_node(*layout, *C, *ntree, *node, 0);
1013 }
1014}
SpaceNode * CTX_wm_space_node(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
void id_us_plus(ID *id)
Definition lib_id.cc:351
#define NODE_TYPES_BEGIN(ntype)
Definition BKE_node.hh:597
#define NODE_CUSTOM_GROUP
Definition BKE_node.hh:807
#define NODE_GROUP
Definition BKE_node.hh:800
#define NODE_CLASS_LAYOUT
Definition BKE_node.hh:420
#define NODE_CLASS_GROUP
Definition BKE_node.hh:409
#define NODE_TYPES_END
Definition BKE_node.hh:605
#define NODE_CLASS_TEXTURE
Definition BKE_node.hh:414
void BKE_ntree_update_tag_node_property(bNodeTree *ntree, bNode *node)
#define ATTR_FALLTHROUGH
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
int char char int int int BLI_strcasecmp_natural(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define ELEM(...)
#define STREQ(a, b)
#define RPT_(msgid)
#define TIP_(msgid)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
@ NTREE_GEOMETRY
@ NODE_TEST
@ NODE_ACTIVE_TEXTURE
@ SOCK_OUT
@ SOCK_IN
@ SOCK_HIDE_VALUE
@ SOCK_COLLAPSED
@ SOCK_UNAVAIL
@ SOCK_INT
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_SHADER
@ SOCK_FLOAT
@ SOCK_CUSTOM
@ SOCK_ROTATION
@ SOCK_STRING
@ SOCK_RGBA
@ SOCK_MENU
@ NODE_PANEL_COLLAPSED
void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *ntree)
Definition node_edit.cc:492
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:104
static void split(const char *text, const char *seps, char ***str, int *count)
Read Guarded memory(de)allocation.
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
#define UI_UNIT_Y
@ UI_EMBOSS_NONE
@ UI_EMBOSS
@ UI_LAYOUT_ALIGN_RIGHT
uiPropertySplitWrapper uiItemPropertySplitWrapperCreate(uiLayout *parent_layout)
uiBut * uiDefBut(uiBlock *block, int type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, const char *tip)
void uiItemL(uiLayout *layout, const char *name, int icon)
void uiTemplateNodeLink(uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
uiBlock * uiLayoutGetBlock(uiLayout *layout)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void UI_but_drawflag_enable(uiBut *but, int flag)
void uiLayoutSetAlignment(uiLayout *layout, char alignment)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemS(uiLayout *layout)
uiBut * uiDefIconTextBut(uiBlock *block, int type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, const char *tip)
#define UI_ITEM_NONE
void UI_but_type_set_menu_from_pulldown(uiBut *but)
void UI_block_emboss_set(uiBlock *block, eUIEmbossType emboss)
uiBut * uiDefMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, blender::StringRef str, int x, int y, short width, short height, const char *tip)
void uiItemDecoratorR(uiLayout *layout, PointerRNA *ptr, const char *propname, int index)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
uiLayout * uiLayoutSplit(uiLayout *layout, float percentage, bool align)
void UI_block_layout_set_current(uiBlock *block, uiLayout *layout)
#define UI_UNIT_X
@ UI_BTYPE_BUT
@ UI_BTYPE_LABEL
#define UI_MAX_NAME_STR
uiBut * uiDefIconMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, int icon, int x, int y, short width, short height, const char *tip)
void UI_but_funcN_set(uiBut *but, uiButHandleNFunc funcN, void *argN, void *arg2, uiButArgNFree func_argN_free_fn=MEM_freeN, uiButArgNCopy func_argN_copy_fn=MEM_dupallocN)
void UI_but_node_link_set(uiBut *but, bNodeSocket *socket, const float draw_color[4])
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_BUT_ICON_LEFT
@ UI_BUT_NO_TOOLTIP
@ UI_BUT_TEXT_LEFT
@ UI_ITEM_R_ICON_ONLY
@ UI_BUT_NODE_ACTIVE
#define ND_SPACE_NODE_VIEW
Definition WM_types.hh:502
#define NC_SPACE
Definition WM_types.hh:359
const T * iterator
Definition BLI_span.hh:82
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
int64_t size() const
void append(const T &value)
local_group_size(16, 16) .push_constant(Type b
OperationNode * node
#define RNA_NO_INDEX
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
static ulong * next
void node_set_active(bNodeTree *ntree, bNode *node)
Definition node.cc:3896
bNode * node_add_static_node(const bContext *C, bNodeTree *ntree, int type)
Definition node.cc:2642
void node_remove_node(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user)
Definition node.cc:3545
bool node_group_poll(const bNodeTree *nodetree, const bNodeTree *grouptree, const char **r_disabled_hint)
void node_position_relative(bNode *from_node, const bNode *to_node, const bNodeSocket *from_sock, const bNodeSocket *to_sock)
Definition node.cc:3136
bNodeLink * node_add_link(bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, bNode *tonode, bNodeSocket *tosock)
Definition node.cc:2912
void nodeLabel(const bNodeTree *ntree, const bNode *node, char *label, int maxlen)
Definition node.cc:4267
void node_remove_link(bNodeTree *ntree, bNodeLink *link)
Definition node.cc:2958
int node_socket_link_limit(const bNodeSocket *sock)
Definition node.cc:3925
int node_count_socket_links(const bNodeTree *ntree, const bNodeSocket *sock)
Definition node.cc:3838
static void node_clear_recursive(bNode *node)
static void ui_node_draw_panel(uiLayout &layout, bContext &C, bNodeTree &ntree, const nodes::PanelDeclaration &panel_decl, bNodePanelState &panel_state, PointerRNA nodeptr)
static void node_socket_disconnect(Main *bmain, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to)
static void node_link_item_apply(bNodeTree *ntree, bNode *node, NodeLinkItem *item)
static int ui_node_item_name_compare(const void *a, const void *b)
static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_p)
static void node_tag_recursive(bNode *node)
static Vector< NodeLinkItem > ui_node_link_items(NodeLinkArg *arg, int in_out, std::optional< NodeDeclaration > &r_node_decl)
static void node_menu_column_foreach_cb(void *calldata, int nclass, const char *name)
static void ui_node_draw_input(uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, bNodeSocket &input, int depth, const char *panel_label)
static void ui_node_sock_name(const bNodeTree *ntree, bNodeSocket *sock, char name[UI_MAX_NAME_STR])
static void node_socket_add_replace(const bContext *C, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to, int type, NodeLinkItem *item)
static void node_remove_linked(Main *bmain, bNodeTree *ntree, bNode *rem_node)
static void ui_node_draw_node(uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, int depth)
const char * node_socket_get_label(const bNodeSocket *socket, const char *panel_label)
Definition node_draw.cc:432
static int ui_compatible_sockets(int typeA, int typeB)
static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
static void node_socket_remove(Main *bmain, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to)
static bool node_link_item_compare(bNode *node, NodeLinkItem *item)
static bool ui_node_item_special_poll(const bNodeTree *, const bke::bNodeType *ntype)
static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, void *ntree_argv)
void node_geometry_add_attribute_search_button(const bContext &, const bNode &node, PointerRNA &socket_ptr, uiLayout &layout, const StringRefNull placeholder)
static void ui_node_link(bContext *C, void *arg_p, void *event_p)
void node_socket_add_tooltip(const bNodeTree &ntree, const bNodeSocket &sock, uiLayout &layout)
static void node_link_item_init(NodeLinkItem &item)
void build_node_declaration(const bke::bNodeType &typeinfo, NodeDeclaration &r_declaration, const bNodeTree *ntree, const bNode *node)
void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
#define UI_NODE_LINK_DISCONNECT
void uiTemplateNodeView(uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
void uiTemplateNodeLink(uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
#define UI_NODE_LINK_REMOVE
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
Definition DNA_ID.h:413
void * next
Definition DNA_ID.h:416
char name[66]
Definition DNA_ID.h:425
void * last
void * first
ListBase nodetrees
Definition BKE_main.hh:234
ID * owner_id
Definition RNA_types.hh:40
struct bNodeLink * link
bNodeTreeTypeHandle * typeinfo
ListBase nodes
bNodeTypeHandle * typeinfo
float locy
ListBase inputs
float locx
struct bNode * next
void * storage
float offsetx
ListBase outputs
float offsety
int16_t type
Compact definition of a node socket.
Definition BKE_node.hh:103
Defines a socket type.
Definition BKE_node.hh:151
void(* foreach_nodeclass)(void *calldata, bNodeClassCallback func)
Definition BKE_node.hh:461
Defines a node type.
Definition BKE_node.hh:218
bNodeSocketTemplate * inputs
Definition BKE_node.hh:233
bNodeSocketTemplate * outputs
Definition BKE_node.hh:233
NodeDeclareFunction declare
Definition BKE_node.hh:347
ListBase buttons
uiButArgNFree func_argN_free_fn
uiButArgNCopy func_argN_copy_fn
void * func_argN
uiItemInternalFlag flag
#define N_(msgid)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)