Blender V5.0
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
8
9#include <cstdlib>
10#include <cstring>
11#include <optional>
12
13#include <fmt/format.h>
14
15#include "MEM_guardedalloc.h"
16
17#include "DNA_node_types.h"
18#include "DNA_screen_types.h"
19
20#include "BLI_listbase.h"
21#include "BLI_string.h"
22#include "BLI_string_utf8.h"
23#include "BLI_vector.hh"
24
25#include "BLT_translation.hh"
26
27#include "BKE_context.hh"
28#include "BKE_lib_id.hh"
29#include "BKE_library.hh"
30#include "BKE_main.hh"
32#include "BKE_node_runtime.hh"
34
35#include "RNA_access.hh"
36#include "RNA_prototypes.hh"
37
39#include "NOD_socket.hh"
40
41#include "../interface/interface_intern.hh" /* XXX bad level */
43
44#include "ED_node.hh" /* own include */
45#include "node_intern.hh"
46
47#include "ED_undo.hh"
48
49#include "WM_api.hh"
50
52
54
55/************************* Node Socket Manipulation **************************/
56
57/* describes an instance of a node type and a specific socket to link */
59 int socket_index; /* index for linking */
60 int socket_type; /* socket type for compatibility check */
61 const char *socket_name; /* ui label of the socket */
62 const char *node_name; /* ui label of the node */
63
64 /* extra settings */
65 bNodeTree *ngroup; /* group node tree */
66};
67
69{
70 item.socket_index = -1;
72 item.socket_name = nullptr;
73 item.node_name = nullptr;
74 item.ngroup = nullptr;
75}
76
77/* Compare an existing node to a link item to see if it can be reused.
78 * item must be for the same node type!
79 * XXX should become a node type callback
80 */
81static bool node_link_item_compare(bNode *node, NodeLinkItem *item)
82{
83 if (node->is_group()) {
84 return (node->id == (ID *)item->ngroup);
85 }
86 return true;
87}
88
89static void node_link_item_apply(bNodeTree *ntree, bNode *node, NodeLinkItem *item)
90{
91 if (node->is_group()) {
92 node->id = (ID *)item->ngroup;
94 }
95 else {
96 /* nothing to do for now */
97 }
98
99 if (node->id) {
100 id_us_plus(node->id);
101 }
102}
103
104static void node_tag_recursive(bNode *node)
105{
106 if (!node || (node->flag & NODE_TEST)) {
107 return; /* in case of cycles */
108 }
109
110 node->flag |= NODE_TEST;
111
113 if (input->link) {
114 node_tag_recursive(input->link->fromnode);
115 }
116 }
117}
118
119static void node_clear_recursive(bNode *node)
120{
121 if (!node || !(node->flag & NODE_TEST)) {
122 return; /* in case of cycles */
123 }
124
125 node->flag &= ~NODE_TEST;
126
128 if (input->link) {
129 node_clear_recursive(input->link->fromnode);
130 }
131 }
132}
133
134static void node_remove_linked(Main *bmain, bNodeTree *ntree, bNode *rem_node)
135{
136 bNode *node, *next;
137
138 if (!rem_node) {
139 return;
140 }
141
142 /* tag linked nodes to be removed */
143 for (bNode *node : ntree->all_nodes()) {
144 node->flag &= ~NODE_TEST;
145 }
146
147 node_tag_recursive(rem_node);
148
149 /* clear tags on nodes that are still used by other nodes */
150 for (bNode *node : ntree->all_nodes()) {
151 if (!(node->flag & NODE_TEST)) {
152 LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
153 if (sock->link && sock->link->fromnode != rem_node) {
154 node_clear_recursive(sock->link->fromnode);
155 }
156 }
157 }
158 }
159
160 /* remove nodes */
161 for (node = (bNode *)ntree->nodes.first; node; node = next) {
162 next = node->next;
163
164 if (node->flag & NODE_TEST) {
165 bke::node_remove_node(bmain, *ntree, *node, true);
166 }
167 }
168}
169
170/* disconnect socket from the node it is connected to */
171static void node_socket_disconnect(Main *bmain,
172 bNodeTree *ntree,
173 bNode *node_to,
174 bNodeSocket *sock_to)
175{
176 if (!sock_to->link) {
177 return;
178 }
179
180 bke::node_remove_link(ntree, *sock_to->link);
181 sock_to->flag |= SOCK_COLLAPSED;
182
184 BKE_main_ensure_invariants(*bmain, ntree->id);
185}
186
187/* remove all nodes connected to this socket, if they aren't connected to other nodes */
188static void node_socket_remove(Main *bmain, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to)
189{
190 if (!sock_to->link) {
191 return;
192 }
193
194 node_remove_linked(bmain, ntree, sock_to->link->fromnode);
195 sock_to->flag |= SOCK_COLLAPSED;
196
198 BKE_main_ensure_invariants(*bmain, ntree->id);
199}
200
201/* add new node connected to this socket, or replace an existing one */
203 bNodeTree *ntree,
204 bNode *node_to,
205 bNodeSocket *sock_to,
206 int type,
207 NodeLinkItem *item)
208{
209 Main *bmain = CTX_data_main(C);
210 bNode *node_from;
211 bNodeSocket *sock_from_tmp;
212 bNode *node_prev = nullptr;
213
214 /* unlink existing node */
215 if (sock_to->link) {
216 node_prev = sock_to->link->fromnode;
217 bke::node_remove_link(ntree, *sock_to->link);
218 }
219
220 /* find existing node that we can use */
221 for (node_from = (bNode *)ntree->nodes.first; node_from; node_from = node_from->next) {
222 if (node_from->type_legacy == type) {
223 break;
224 }
225 }
226
227 if (node_from) {
228 if (node_from->inputs.first || node_from->typeinfo->draw_buttons ||
229 node_from->typeinfo->draw_buttons_ex)
230 {
231 node_from = nullptr;
232 }
233 }
234
235 if (node_prev && node_prev->type_legacy == type && node_link_item_compare(node_prev, item)) {
236 /* keep the previous node if it's the same type */
237 node_from = node_prev;
238 }
239 else if (!node_from) {
240 node_from = bke::node_add_static_node(C, *ntree, type);
241 if (node_prev != nullptr) {
242 /* If we're replacing existing node, use its location. */
243 node_from->location[0] = node_prev->location[0];
244 node_from->location[1] = node_prev->location[1];
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 BKE_main_ensure_invariants(*bmain, ntree->id);
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) >=
267 bke::node_socket_link_limit(*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 BKE_main_ensure_invariants(*bmain, ntree->id);
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 if (arg->node_type->type_legacy == NODE_GROUP) {
331 LISTBASE_FOREACH (bNodeTree *, ngroup, &arg->bmain->nodetrees) {
332 if (BKE_id_name(ngroup->id)[0] == '.') {
333 /* Don't display hidden node groups, just like the add menu. */
334 continue;
335 }
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 ngroup->ensure_interface_cache();
345 Span<bNodeTreeInterfaceSocket *> iosockets = (in_out == SOCK_IN ?
346 ngroup->interface_inputs() :
347 ngroup->interface_outputs());
348 for (const int index : iosockets.index_range()) {
349 bNodeTreeInterfaceSocket *iosock = iosockets[index];
350 NodeLinkItem item;
352 item.socket_index = index;
353 /* NOTE: int stemp->type is not fully reliable, not used for node group
354 * interface sockets. use the typeinfo->type instead.
355 */
356 const bke::bNodeSocketType *typeinfo = iosock->socket_typeinfo();
357 item.socket_type = typeinfo->type;
358 item.socket_name = iosock->name;
359 item.node_name = ngroup->id.name + 2;
360 item.ngroup = ngroup;
361
362 items.append(item);
363 }
364 }
365 }
366 else if (arg->node_type->declare != nullptr) {
367 using namespace blender;
368 using namespace blender::nodes;
369
370 r_node_decl.emplace(NodeDeclaration());
371 blender::nodes::build_node_declaration(*arg->node_type, *r_node_decl, nullptr, nullptr);
372 Span<SocketDeclaration *> socket_decls = (in_out == SOCK_IN) ? r_node_decl->inputs :
373 r_node_decl->outputs;
374 int index = 0;
375 for (const SocketDeclaration *socket_decl_ptr : socket_decls) {
376 const SocketDeclaration &socket_decl = *socket_decl_ptr;
377 NodeLinkItem item;
379 item.socket_index = index++;
380 item.socket_type = socket_decl.socket_type;
381 item.socket_name = socket_decl.name.c_str();
382 item.node_name = arg->node_type->ui_name.c_str();
383 items.append(item);
384 }
385 }
386 else {
387 bke::bNodeSocketTemplate *socket_templates = (in_out == SOCK_IN ? arg->node_type->inputs :
388 arg->node_type->outputs);
390 int i;
391
392 i = 0;
393 for (stemp = socket_templates; stemp && stemp->type != -1; stemp++, i++) {
394 NodeLinkItem item;
396 item.socket_index = i;
397 item.socket_type = stemp->type;
398 item.socket_name = stemp->name;
399 item.node_name = arg->node_type->ui_name.c_str();
400 items.append(item);
401 }
402 }
403
404 return items;
405}
406
407static void ui_node_link(bContext *C, void *arg_p, void *event_p)
408{
409 NodeLinkArg *arg = (NodeLinkArg *)arg_p;
410 Main *bmain = arg->bmain;
411 bNode *node_to = arg->node;
412 bNodeSocket *sock_to = arg->sock;
413 bNodeTree *ntree = arg->ntree;
414 int event = POINTER_AS_INT(event_p);
415
416 if (event == UI_NODE_LINK_DISCONNECT) {
417 node_socket_disconnect(bmain, ntree, node_to, sock_to);
418 }
419 else if (event == UI_NODE_LINK_REMOVE) {
420 node_socket_remove(bmain, ntree, node_to, sock_to);
421 }
422 else {
423 node_socket_add_replace(C, ntree, node_to, sock_to, arg->node_type->type_legacy, &arg->item);
424 }
425
426 ED_undo_push(C, "Node input modify");
427}
428
429static void ui_node_sock_name(const bNodeTree *ntree,
430 bNodeSocket *sock,
431 char name[UI_MAX_NAME_STR])
432{
433 if (sock->link && sock->link->fromnode) {
434 bNode *node = sock->link->fromnode;
435 const std::string node_name = bke::node_label(*ntree, *node);
436
437 if (BLI_listbase_is_empty(&node->inputs) && node->outputs.first != node->outputs.last) {
440 "%s | %s",
441 IFACE_(node_name.c_str()),
442 IFACE_(sock->link->fromsock->name));
443 }
444 else {
445 BLI_strncpy_utf8(name, IFACE_(node_name.c_str()), UI_MAX_NAME_STR);
446 }
447 }
448 else if (sock->type == SOCK_SHADER) {
450 }
451 else {
453 }
454}
455
456static int ui_compatible_sockets(int typeA, int typeB)
457{
458 return (typeA == typeB);
459}
460
461static int ui_node_item_name_compare(const void *a, const void *b)
462{
463 const bke::bNodeType *type_a = *(const bke::bNodeType **)a;
464 const bke::bNodeType *type_b = *(const bke::bNodeType **)b;
465 return BLI_strcasecmp_natural(type_a->ui_name.c_str(), type_b->ui_name.c_str());
466}
467
468static bool ui_node_item_special_poll(const bNodeTree * /*ntree*/, const bke::bNodeType *ntype)
469{
470 if (ntype->idname == "ShaderNodeUVAlongStroke") {
471 /* TODO(sergey): Currently we don't have Freestyle nodes edited from
472 * the buttons context, so can ignore its nodes completely.
473 *
474 * However, we might want to do some extra checks here later.
475 */
476 return false;
477 }
478 return true;
479}
480
481static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
482{
483 bNodeTree *ntree = arg->ntree;
484 bNodeSocket *sock = arg->sock;
485 uiLayout *layout = arg->layout;
486 uiLayout *column = nullptr;
487 uiBlock *block = layout->block();
488 uiBut *but;
489 NodeLinkArg *argN;
490 int first = 1;
491
492 /* generate array of node types sorted by UI name */
494
496 const char *disabled_hint;
497 if (!(ntype->poll && ntype->poll(ntype, ntree, &disabled_hint))) {
498 continue;
499 }
500
501 if (ntype->nclass != nclass) {
502 continue;
503 }
504
505 if (!ui_node_item_special_poll(ntree, ntype)) {
506 continue;
507 }
508
509 sorted_ntypes.append(ntype);
510 }
511
512 qsort(sorted_ntypes.data(),
513 sorted_ntypes.size(),
514 sizeof(bke::bNodeType *),
516
517 /* generate UI */
518 for (int j = 0; j < sorted_ntypes.size(); j++) {
519 bke::bNodeType *ntype = sorted_ntypes[j];
520 char name[UI_MAX_NAME_STR];
521 const char *cur_node_name = nullptr;
522 int num = 0;
523 int icon = ICON_NONE;
524
525 arg->node_type = ntype;
526
527 std::optional<blender::nodes::NodeDeclaration> node_decl;
528 Vector<NodeLinkItem> items = ui_node_link_items(arg, SOCK_OUT, node_decl);
529
530 for (const NodeLinkItem &item : items) {
531 if (ui_compatible_sockets(item.socket_type, sock->type)) {
532 num++;
533 }
534 }
535
536 for (const NodeLinkItem &item : items) {
537 if (!ui_compatible_sockets(item.socket_type, sock->type)) {
538 continue;
539 }
540
541 if (first) {
542 column = &layout->column(false);
543 ui::block_layout_set_current(block, column);
544
545 column->label(IFACE_(cname), ICON_NODE);
546 but = block->buttons.last().get();
547
548 first = 0;
549 }
550
551 if (num > 1) {
552 if (!cur_node_name || !STREQ(cur_node_name, item.node_name)) {
553 cur_node_name = item.node_name;
554 /* XXX Do not use uiLayout::label here,
555 * it would add an empty icon as we are in a menu! */
556 uiDefBut(block,
558 0,
559 IFACE_(cur_node_name),
560 0,
561 0,
562 UI_UNIT_X * 4,
563 UI_UNIT_Y,
564 nullptr,
565 0.0,
566 0.0,
567 "");
568 }
569
570 STRNCPY_UTF8(name, IFACE_(item.socket_name));
571 icon = ICON_BLANK1;
572 }
573 else {
574 STRNCPY_UTF8(name, IFACE_(item.node_name));
575 icon = ICON_NONE;
576 }
577
578 but = uiDefIconTextBut(block,
580 0,
581 icon,
582 name,
583 0,
584 0,
585 UI_UNIT_X * 4,
586 UI_UNIT_Y,
587 nullptr,
588 TIP_("Add node to input"));
589
590 argN = (NodeLinkArg *)MEM_dupallocN(arg);
591 argN->item = item;
592 UI_but_funcN_set(but, ui_node_link, argN, nullptr);
593 }
594 }
595}
596
597static void node_menu_column_foreach_cb(void *calldata, int nclass, const StringRefNull name)
598{
599 NodeLinkArg *arg = (NodeLinkArg *)calldata;
600
601 if (!ELEM(nclass, NODE_CLASS_GROUP, NODE_CLASS_LAYOUT)) {
602 ui_node_menu_column(arg, nclass, name.c_str());
603 }
604}
605
606static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_p)
607{
608 Main *bmain = CTX_data_main(C);
609 Scene *scene = CTX_data_scene(C);
610 uiBlock *block = layout->block();
611 uiBut *but = (uiBut *)but_p;
612 uiLayout *split, *column;
613 NodeLinkArg *arg = (NodeLinkArg *)but->func_argN;
614 bNodeSocket *sock = arg->sock;
615 bke::bNodeTreeType *ntreetype = arg->ntree->typeinfo;
616
617 ui::block_layout_set_current(block, layout);
618 split = &layout->split(0.0f, false);
619
620 arg->bmain = bmain;
621 arg->scene = scene;
622 arg->layout = split;
623
624 if (ntreetype && ntreetype->foreach_nodeclass) {
626 }
627
628 column = &split->column(false);
629 ui::block_layout_set_current(block, column);
630
631 if (sock->link) {
632 column->label(IFACE_("Link"), ICON_NONE);
633 but = block->buttons.last().get();
635
636 but = uiDefBut(block,
638 0,
640 0,
641 0,
642 UI_UNIT_X * 4,
643 UI_UNIT_Y,
644 nullptr,
645 0.0,
646 0.0,
647 TIP_("Remove nodes connected to the input"));
649
650 but = uiDefBut(block,
652 0,
653 IFACE_("Disconnect"),
654 0,
655 0,
656 UI_UNIT_X * 4,
657 UI_UNIT_Y,
658 nullptr,
659 0.0,
660 0.0,
661 TIP_("Disconnect nodes connected to the input"));
664 }
665
667}
668
669} // namespace blender::ed::space_node
670
672 uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
673{
674 using namespace blender::ed::space_node;
675
676 uiBlock *block = layout->block();
677 NodeLinkArg *arg;
678 uiBut *but;
679 float socket_col[4];
680
681 arg = MEM_callocN<NodeLinkArg>("NodeLinkArg");
682 arg->ntree = ntree;
683 arg->node = node;
684 arg->sock = input;
686
687 PointerRNA node_ptr = RNA_pointer_create_discrete(&ntree->id, &RNA_Node, node);
688 node_socket_color_get(*C, *ntree, node_ptr, *input, socket_col);
689
691
692 if (input->link || input->type == SOCK_SHADER || (input->flag & SOCK_HIDE_VALUE)) {
693 char name[UI_MAX_NAME_STR];
695 but = uiDefMenuBut(
696 block, ui_template_node_link_menu, nullptr, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
697 }
698 else {
699 but = uiDefIconMenuBut(
700 block, ui_template_node_link_menu, nullptr, ICON_NONE, 0, 0, UI_UNIT_X, UI_UNIT_Y, "");
701 }
702
704 UI_but_node_link_set(but, input, socket_col);
706
707 but->poin = (char *)but;
708 but->func_argN = arg;
711
712 if (input->link && input->link->fromnode) {
713 if (input->link->fromnode->flag & NODE_ACTIVE_TEXTURE) {
714 but->flag |= UI_BUT_NODE_ACTIVE;
715 }
716 }
717
718 if (!ID_IS_EDITABLE(ntree)) {
719 UI_but_disable(but, "Cannot edit linked node tree");
720 }
721}
722
723namespace blender::ed::space_node {
724
725/**************************** Node Tree Layout *******************************/
726
727static void ui_node_draw_input(uiLayout &layout,
728 bContext &C,
729 bNodeTree &ntree,
730 bNode &node,
732 int depth,
733 const char *panel_label);
734
736 bContext &C,
737 bNodeTree &ntree,
738 bNode &node,
739 const nodes::PanelDeclaration &panel_decl,
740 const int depth)
741{
742 const nodes::SocketDeclaration *panel_toggle_decl = panel_decl.panel_input_decl();
743 const std::string panel_id = fmt::format(
744 "{}_{}_{}", ntree.id.name, node.identifier, panel_decl.identifier);
745 const StringRef panel_translation_context = panel_decl.translation_context.has_value() ?
746 *panel_decl.translation_context :
747 "";
748 PanelLayout panel_layout = layout.panel(&C, panel_id.c_str(), panel_decl.default_collapsed);
749 if (panel_toggle_decl) {
750 panel_layout.header->use_property_split_set(false);
751 panel_layout.header->use_property_decorate_set(false);
753 &ntree.id, &RNA_NodeSocket, &node.socket_by_decl(*panel_toggle_decl));
754 panel_layout.header->prop(&toggle_ptr,
755 "default_value",
757 CTX_IFACE_(panel_translation_context, panel_decl.name),
758 ICON_NONE);
759 }
760 else {
761 panel_layout.header->label(CTX_IFACE_(panel_translation_context, panel_decl.name), ICON_NONE);
762 }
763
764 if (!panel_layout.body) {
765 return;
766 }
767 for (const nodes::ItemDeclaration *item_decl : panel_decl.items) {
768 if (item_decl == panel_toggle_decl) {
769 continue;
770 }
771 if (const auto *socket_decl = dynamic_cast<const nodes::SocketDeclaration *>(item_decl)) {
772 bNodeSocket &socket = node.socket_by_decl(*socket_decl);
773 if (socket_decl->custom_draw_fn) {
775 C,
776 *panel_layout.body,
777 ntree,
778 node,
779 socket,
780 RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &node),
781 RNA_pointer_create_discrete(&ntree.id, &RNA_NodeSocket, &socket)};
782 (*socket_decl->custom_draw_fn)(params);
783 }
784 else if (socket_decl->in_out == SOCK_IN) {
786 *panel_layout.body, C, ntree, node, socket, depth, panel_decl.name.c_str());
787 }
788 }
789 else if (const auto *sub_panel_decl = dynamic_cast<const nodes::PanelDeclaration *>(item_decl))
790 {
791 ui_node_draw_recursive(*panel_layout.body, C, ntree, node, *sub_panel_decl, depth + 1);
792 }
793 else if (const auto *layout_decl = dynamic_cast<const nodes::LayoutDeclaration *>(item_decl)) {
794 PointerRNA nodeptr = RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &node);
795 layout_decl->draw(panel_layout.body, &C, &nodeptr);
796 }
797 }
798}
799
801 uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, int depth)
802{
803 PointerRNA nodeptr = RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &node);
804
805 /* Draw top-level node buttons. */
806 if (node.typeinfo->draw_buttons) {
807 if (node.type_legacy != NODE_GROUP) {
808 layout.use_property_split_set(true);
809 node.typeinfo->draw_buttons(&layout, &C, &nodeptr);
810 }
811 }
812
813 if (node.declaration()) {
814 const nodes::NodeDeclaration &node_decl = *node.declaration();
815 for (const nodes::ItemDeclaration *item_decl : node_decl.root_items) {
816 if (const auto *panel_decl = dynamic_cast<const nodes::PanelDeclaration *>(item_decl)) {
817 ui_node_draw_recursive(layout, C, ntree, node, *panel_decl, depth + 1);
818 }
819 else if (const auto *socket_decl = dynamic_cast<const nodes::SocketDeclaration *>(item_decl))
820 {
821 bNodeSocket &socket = node.socket_by_decl(*socket_decl);
822 if (socket_decl->custom_draw_fn) {
824 C,
825 layout,
826 ntree,
827 node,
828 socket,
829 RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &node),
830 RNA_pointer_create_discrete(&ntree.id, &RNA_NodeSocket, &socket)};
831 (*socket_decl->custom_draw_fn)(params);
832 }
833 else if (socket_decl->in_out == SOCK_IN) {
834 ui_node_draw_input(layout, C, ntree, node, socket, depth + 1, nullptr);
835 }
836 }
837 else if (const auto *layout_decl = dynamic_cast<const nodes::LayoutDeclaration *>(item_decl))
838 {
839 if (!layout_decl->is_default) {
840 PointerRNA nodeptr = RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &node);
841 layout_decl->draw(&layout, &C, &nodeptr);
842 }
843 }
844 }
845 }
846 else {
847 /* Draw socket values using the flat inputs list. */
849 ui_node_draw_input(layout, C, ntree, node, *input, depth + 1, nullptr);
850 }
851 }
852}
853
854static void ui_node_draw_input(uiLayout &layout,
855 bContext &C,
856 bNodeTree &ntree,
857 bNode &node,
859 int depth,
860 const char *panel_label)
861{
862 uiBlock *block = layout.block();
863 uiLayout *row = nullptr;
864 bool dependency_loop;
865
866 if (input.flag & SOCK_UNAVAIL) {
867 return;
868 }
869
870 /* to avoid eternal loops on cyclic dependencies */
871 node.flag |= NODE_TEST;
872 bNode *lnode = (input.link) ? input.link->fromnode : nullptr;
873
874 dependency_loop = (lnode && (lnode->flag & NODE_TEST));
875 if (dependency_loop) {
876 lnode = nullptr;
877 }
878
879 /* socket RNA pointer */
880 PointerRNA inputptr = RNA_pointer_create_discrete(&ntree.id, &RNA_NodeSocket, &input);
881 PointerRNA nodeptr = RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &node);
882
883 row = &layout.row(true);
884
886 /* Decorations are added manually here. */
887 row->use_property_decorate_set(false);
888 /* Empty decorator item for alignment. */
889 bool add_dummy_decorator = false;
890
891 {
892 uiLayout *sub = &split_wrapper.label_column->row(true);
893
894 if (depth > 0) {
896
897 if (lnode) {
898 /* Input linked to a node, we can expand/collapse if
899 * - linked node has inputs
900 * - linked node has dedicated button drawing
901 * - linked node has dedicated socket drawing */
902 bool can_expand = lnode->inputs.first;
903 if (lnode->type_legacy != NODE_GROUP) {
904 if (lnode->typeinfo->draw_buttons) {
905 can_expand = true;
906 }
907 else if (lnode->declaration()) {
908 const nodes::NodeDeclaration &lnode_decl = *lnode->declaration();
909 for (const nodes::ItemDeclaration *item_decl : lnode_decl.root_items) {
910 if (const auto *socket_decl = dynamic_cast<const nodes::SocketDeclaration *>(
911 item_decl))
912 {
913 if (socket_decl->custom_draw_fn) {
914 can_expand = true;
915 }
916 }
917 }
918 }
919 }
920 if (can_expand) {
921 int icon = (input.flag & SOCK_COLLAPSED) ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT;
922 sub->prop(&inputptr, "show_expanded", UI_ITEM_R_ICON_ONLY, "", icon);
923 }
924 }
925
927 }
928
929 sub = &sub->row(true);
931 sub->label(node_socket_get_label(&input, panel_label), ICON_NONE);
932 }
933
934 if (dependency_loop) {
935 row->label(RPT_("Dependency Loop"), ICON_ERROR);
936 add_dummy_decorator = true;
937 }
938 else if (lnode) {
939 /* input linked to a node */
940 uiTemplateNodeLink(row, &C, &ntree, &node, &input);
941 add_dummy_decorator = true;
942
943 if (depth == 0 || !(input.flag & SOCK_COLLAPSED)) {
944 if (depth == 0) {
945 layout.separator();
946 }
947
948 ui_node_draw_node(layout, C, ntree, *lnode, depth);
949 }
950 }
951 else {
952 uiLayout *sub = &row->row(true);
953
954 uiTemplateNodeLink(sub, &C, &ntree, &node, &input);
955
956 if (input.flag & SOCK_HIDE_VALUE) {
957 add_dummy_decorator = true;
958 }
959 /* input not linked, show value */
960 else {
961 switch (input.type) {
962 case SOCK_VECTOR:
963 sub->separator();
964 sub = &sub->column(true);
966 case SOCK_FLOAT:
967 case SOCK_INT:
968 case SOCK_ROTATION:
969 case SOCK_BOOLEAN:
970 case SOCK_RGBA:
971 case SOCK_MENU:
972 sub->prop(&inputptr, "default_value", UI_ITEM_NONE, "", ICON_NONE);
973 if (split_wrapper.decorate_column) {
974 split_wrapper.decorate_column->decorator(&inputptr, "default_value", RNA_NO_INDEX);
975 }
976 break;
977 case SOCK_STRING: {
978 const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id;
979 SpaceNode *snode = CTX_wm_space_node(&C);
980 if (node_tree->type == NTREE_GEOMETRY && snode != nullptr) {
981 /* Only add the attribute search in the node editor, in other places there is not
982 * enough context. */
983 node_geometry_add_attribute_search_button(C, node, inputptr, *sub);
984 }
985 else {
986 sub->prop(&inputptr, "default_value", UI_ITEM_NONE, "", ICON_NONE);
987 }
988 if (split_wrapper.decorate_column) {
989 split_wrapper.decorate_column->decorator(&inputptr, "default_value", RNA_NO_INDEX);
990 }
991 break;
992 }
993 case SOCK_CUSTOM:
994 input.typeinfo->draw(&C, sub, &inputptr, &nodeptr, input.name);
995 break;
996 default:
997 add_dummy_decorator = true;
998 }
999 }
1000 }
1001
1002 if (add_dummy_decorator && split_wrapper.decorate_column) {
1003 split_wrapper.decorate_column->decorator(nullptr, std::nullopt, 0);
1004 }
1005
1006 node_socket_add_tooltip(ntree, input, *row);
1007
1008 /* clear */
1009 node.flag &= ~NODE_TEST;
1010}
1011
1012} // namespace blender::ed::space_node
1013
1015 uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
1016{
1017 using namespace blender::ed::space_node;
1018
1019 if (!ntree) {
1020 return;
1021 }
1022 ntree->ensure_topology_cache();
1023
1024 /* clear for cycle check */
1025 for (bNode *tnode : ntree->all_nodes()) {
1026 tnode->flag &= ~NODE_TEST;
1027 }
1028
1029 if (input) {
1030 ui_node_draw_input(*layout, *C, *ntree, *node, *input, 0, nullptr);
1031 }
1032 else {
1033 ui_node_draw_node(*layout, *C, *ntree, *node, 0);
1034 }
1035}
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:358
const char * BKE_id_name(const ID &id)
void BKE_main_ensure_invariants(Main &bmain, std::optional< blender::Span< ID * > > modified_ids=std::nullopt)
#define NODE_GROUP
Definition BKE_node.hh:811
#define NODE_CLASS_LAYOUT
Definition BKE_node.hh:463
#define NODE_CLASS_GROUP
Definition BKE_node.hh:452
#define NODE_CLASS_TEXTURE
Definition BKE_node.hh:457
void BKE_ntree_update_tag_node_property(bNodeTree *ntree, bNode *node)
#define ATTR_FALLTHROUGH
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
ATTR_WARN_UNUSED_RESULT const size_t num
int char char int int int BLI_strcasecmp_natural(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
size_t size_t size_t BLI_snprintf_utf8(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) 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)
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:705
@ NODE_TEST
@ NODE_ACTIVE_TEXTURE
@ NTREE_GEOMETRY
@ SOCK_OUT
@ SOCK_IN
@ SOCK_HIDE_VALUE
@ SOCK_COLLAPSED
@ SOCK_UNAVAIL
struct bNode bNode
@ SOCK_INT
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_SHADER
@ SOCK_FLOAT
@ SOCK_CUSTOM
@ SOCK_ROTATION
@ SOCK_STRING
@ SOCK_RGBA
@ SOCK_MENU
struct bNodeTree bNodeTree
struct bNodeSocket bNodeSocket
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)
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
void UI_but_disable(uiBut *but, const char *disabled_hint)
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)
#define UI_UNIT_Y
void UI_block_emboss_set(uiBlock *block, blender::ui::EmbossType emboss)
uiBut * uiDefMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, blender::StringRef str, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
@ UI_BUT_NODE_ACTIVE
void uiTemplateNodeLink(uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
void UI_but_drawflag_enable(uiBut *but, int flag)
void UI_but_type_set_menu_from_pulldown(uiBut *but)
uiBut * uiDefIconMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, int icon, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
#define UI_UNIT_X
@ UI_BUT_ICON_LEFT
@ UI_BUT_TEXT_LEFT
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])
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
@ UI_ITEM_R_ICON_ONLY
uiPropertySplitWrapper uiItemPropertySplitWrapperCreate(uiLayout *parent_layout)
#define UI_ITEM_NONE
#define UI_MAX_NAME_STR
eNodeSocketDatatype socket_type
const T & last(const int64_t n=0) const
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
int64_t size() const
void append(const T &value)
Vector< ItemDeclaration * > root_items
std::optional< std::string > translation_context
Vector< ItemDeclaration * > items
const SocketDeclaration * panel_input_decl() const
#define input
#define RNA_NO_INDEX
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong * next
std::string node_label(const bNodeTree &ntree, const bNode &node)
Definition node.cc:4970
void node_remove_node(Main *bmain, bNodeTree &ntree, bNode &node, bool do_id_user, bool remove_animation=true)
Definition node.cc:4386
bool node_group_poll(const bNodeTree *nodetree, const bNodeTree *grouptree, const char **r_disabled_hint)
void node_remove_link(bNodeTree *ntree, bNodeLink &link)
Definition node.cc:3847
Span< bNodeType * > node_types_get()
Definition node.cc:2447
int node_count_socket_links(const bNodeTree &ntree, const bNodeSocket &sock)
Definition node.cc:4674
void node_position_relative(bNode &from_node, const bNode &to_node, const bNodeSocket *from_sock, const bNodeSocket &to_sock)
Definition node.cc:3995
bNode * node_add_static_node(const bContext *C, bNodeTree &ntree, int type)
Definition node.cc:3500
bNodeLink & node_add_link(bNodeTree &ntree, bNode &fromnode, bNodeSocket &fromsock, bNode &tonode, bNodeSocket &tosock)
Definition node.cc:3810
void node_set_active(bNodeTree &ntree, bNode &node)
Definition node.cc:4724
int node_socket_link_limit(const bNodeSocket &sock)
Definition node.cc:4753
static void node_clear_recursive(bNode *node)
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 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])
void node_socket_color_get(const bContext &C, const bNodeTree &ntree, PointerRNA &node_ptr, const bNodeSocket &sock, float r_color[4])
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)
void node_geometry_add_attribute_search_button(const bContext &, const bNode &node, PointerRNA &socket_ptr, uiLayout &layout, const StringRef placeholder)
static int ui_compatible_sockets(int typeA, int typeB)
const char * node_socket_get_label(const bNodeSocket *socket, const char *panel_label=nullptr)
Definition node_draw.cc:453
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 void ui_node_draw_recursive(uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, const nodes::PanelDeclaration &panel_decl, const int depth)
static void node_menu_column_foreach_cb(void *calldata, int nclass, const StringRefNull name)
static bool ui_node_item_special_poll(const bNodeTree *, const bke::bNodeType *ntype)
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 block_layout_set_current(uiBlock *block, uiLayout *layout)
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
const char * name
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
void * last
void * first
ListBase nodetrees
Definition BKE_main.hh:301
ID * owner_id
Definition RNA_types.hh:51
struct bNodeLink * link
bNodeTreeTypeHandle * typeinfo
ListBase nodes
float location[2]
bNodeTypeHandle * typeinfo
ListBase inputs
struct ID * id
int16_t type_legacy
struct bNode * next
void * storage
ListBase outputs
int32_t identifier
Compact definition of a node socket.
Definition BKE_node.hh:99
Defines a socket type.
Definition BKE_node.hh:158
eNodeSocketDatatype type
Definition BKE_node.hh:193
void(* foreach_nodeclass)(void *calldata, bNodeClassCallback func)
Definition BKE_node.hh:507
Defines a node type.
Definition BKE_node.hh:238
bNodeSocketTemplate * inputs
Definition BKE_node.hh:254
bNodeSocketTemplate * outputs
Definition BKE_node.hh:254
NodeDeclareFunction declare
Definition BKE_node.hh:362
blender::Vector< std::unique_ptr< uiBut > > buttons
uiButArgNFree func_argN_free_fn
uiButArgNCopy func_argN_copy_fn
void * func_argN
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(const bContext *C, blender::StringRef idname, bool default_closed)
uiBlock * block() const
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
uiLayout & row(bool align)
uiLayout & split(float percentage, bool align)
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)
i
Definition text_draw.cc:230
#define N_(msgid)