Blender V5.0
node_group.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstdlib>
10
11#include "MEM_guardedalloc.h"
12
13#include "DNA_anim_types.h"
14#include "DNA_node_types.h"
15
16#include "BLI_listbase.h"
17#include "BLI_map.hh"
18#include "BLI_math_vector.h"
19#include "BLI_math_vector.hh"
21#include "BLI_rand.hh"
22#include "BLI_set.hh"
23#include "BLI_string.h"
24#include "BLI_string_utf8.h"
25#include "BLI_vector.hh"
26
27#include "BLT_translation.hh"
28
29#include "BKE_action.hh"
30#include "BKE_animsys.h"
31#include "BKE_context.hh"
32#include "BKE_lib_id.hh"
33#include "BKE_library.hh"
34#include "BKE_main.hh"
36#include "BKE_node_runtime.hh"
38#include "BKE_report.hh"
39
40#include "ANIM_action.hh"
41
43
44#include "ED_node.hh"
45#include "ED_node_preview.hh"
46#include "ED_render.hh"
47#include "ED_screen.hh"
48
49#include "RNA_access.hh"
50#include "RNA_define.hh"
51#include "RNA_path.hh"
52#include "RNA_prototypes.hh"
53
54#include "WM_api.hh"
55#include "WM_types.hh"
56
57#include "UI_resources.hh"
58
59#include "NOD_common.hh"
60#include "NOD_composite.hh"
61#include "NOD_geometry.hh"
63#include "NOD_shader.h"
64#include "NOD_socket.hh"
65#include "NOD_texture.h"
66
67#include "node_intern.hh" /* own include */
68
70
71/* -------------------------------------------------------------------- */
74
76{
79
80 /* Group operators only defined for standard node tree types.
81 * Disabled otherwise to allow python-nodes define their own operators
82 * with same key-map. */
83 if (STR_ELEM(snode->tree_idname,
84 "ShaderNodeTree",
85 "CompositorNodeTree",
86 "TextureNodeTree",
87 "GeometryNodeTree"))
88 {
89 return true;
90 }
91 }
92 return false;
93}
94
96{
99
100 /* Group operators only defined for standard node tree types.
101 * Disabled otherwise to allow python-nodes define their own operators
102 * with same key-map. */
103 if (ED_node_is_shader(snode) || ED_node_is_compositor(snode) || ED_node_is_texture(snode) ||
104 ED_node_is_geometry(snode))
105 {
106 return true;
107 }
108 }
109 return false;
110}
111
113{
114 SpaceNode *snode = CTX_wm_space_node(C);
115 return snode->tree_idname;
116}
117
119{
120 SpaceNode *snode = CTX_wm_space_node(C);
121
122 if (ED_node_is_shader(snode)) {
123 return ntreeType_Shader->group_idname;
124 }
125 if (ED_node_is_compositor(snode)) {
126 return ntreeType_Composite->group_idname;
127 }
128 if (ED_node_is_texture(snode)) {
129 return ntreeType_Texture->group_idname;
130 }
131 if (ED_node_is_geometry(snode)) {
132 return ntreeType_Geometry->group_idname;
133 }
134
135 return "";
136}
137
138static bNode *node_group_get_active(bContext *C, const StringRef node_idname)
139{
140 SpaceNode *snode = CTX_wm_space_node(C);
141 bNode *node = bke::node_get_active(*snode->edittree);
142
143 if (node && node->idname == node_idname) {
144 return node;
145 }
146 return nullptr;
147}
148
149/* Maps old to new identifiers for simulation input node pairing. */
150static void remap_pairing(bNodeTree &dst_tree,
152 const Map<int32_t, int32_t> &identifier_map)
153{
154 for (bNode *dst_node : nodes) {
155 if (bke::all_zone_input_node_types().contains(dst_node->type_legacy)) {
156 const bke::bNodeZoneType &zone_type = *bke::zone_type_by_node_type(dst_node->type_legacy);
157 int &output_node_id = zone_type.get_corresponding_output_id(*dst_node);
158 if (output_node_id == 0) {
159 continue;
160 }
161 output_node_id = identifier_map.lookup_default(output_node_id, 0);
162 if (output_node_id == 0) {
164 }
165 }
166 }
167}
168
170
171/* -------------------------------------------------------------------- */
174
176{
177 SpaceNode *snode = CTX_wm_space_node(C);
178 ARegion *region = CTX_wm_region(C);
179 const StringRef node_idname = node_group_idname(C);
180 const bool exit = RNA_boolean_get(op->ptr, "exit");
181
183
184 bNode *gnode = node_group_get_active(C, node_idname);
185
186 if (gnode && !exit) {
187 bNodeTree *ngroup = (bNodeTree *)gnode->id;
188
189 if (ngroup) {
190 ED_node_tree_push(region, snode, ngroup, gnode);
191 }
192 }
193 else {
194 ED_node_tree_pop(region, snode);
195 }
196
199
200 return OPERATOR_FINISHED;
201}
202
204{
205 /* identifiers */
206 ot->name = "Edit Group";
207 ot->description = "Edit node group";
208 ot->idname = "NODE_OT_group_edit";
209
210 /* API callbacks. */
211 ot->exec = node_group_edit_exec;
213
214 /* flags */
216
217 PropertyRNA *prop = RNA_def_boolean(ot->srna, "exit", false, "Exit", "");
219}
220
222
223/* -------------------------------------------------------------------- */
226
228 wmOperator * /*op*/,
229 const wmEvent *event)
230{
231 SpaceNode &snode = *CTX_wm_space_node(C);
232 ARegion &region = *CTX_wm_region(C);
233
234 /* Don't interfer when the mouse is interacting with some button. See #147282. */
235 if (ISMOUSE_BUTTON(event->type) && UI_but_find_mouse_over(&region, event)) {
237 }
238
239 float2 cursor;
240 UI_view2d_region_to_view(&region.v2d, event->mval[0], event->mval[1], &cursor.x, &cursor.y);
241 bNode *node = node_under_mouse_get(snode, cursor);
242
243 if (!node || node->is_frame()) {
244 ED_node_tree_pop(&region, &snode);
245 return OPERATOR_FINISHED;
246 }
247 if (!node->is_group()) {
249 }
250 if (node->is_custom_group()) {
252 }
253 bNodeTree *group = id_cast<bNodeTree *>(node->id);
254 if (!group || ID_MISSING(group)) {
256 }
257 ED_node_tree_push(&region, &snode, group, node);
258 return OPERATOR_FINISHED;
259}
260
262{
263 ot->name = "Enter/Exit Group";
264 ot->description = "Enter or exit node group based on cursor location";
265 ot->idname = "NODE_OT_group_enter_exit";
266
269
270 ot->flag = OPTYPE_REGISTER;
271}
272
274
275/* -------------------------------------------------------------------- */
278
284 const StringRef dst_basepath)
285{
287 sizeof(*basepath_change), AT);
288 basepath_change->src_basepath = BLI_strdupn(src_basepath.data(), src_basepath.size());
289 basepath_change->dst_basepath = BLI_strdupn(dst_basepath.data(), dst_basepath.size());
290 return basepath_change;
291}
292
294{
295 if (basepath_change->src_basepath != basepath_change->dst_basepath) {
296 MEM_freeN(basepath_change->src_basepath);
297 }
298 MEM_freeN(basepath_change->dst_basepath);
299 MEM_freeN(basepath_change);
300}
301
303 const bNodeTree &ngroup,
304 const bNode &gnode,
305 const Map<int32_t, int32_t> &node_identifier_map)
306{
307 for (bNestedNodeRef &ref : ntree.nested_node_refs_span()) {
308 if (ref.path.node_id != gnode.identifier) {
309 continue;
310 }
311 const bNestedNodeRef *child_ref = ngroup.find_nested_node_ref(ref.path.id_in_node);
312 if (!child_ref) {
313 continue;
314 }
315 constexpr int32_t missing_id = -1;
316 const int32_t new_node_id = node_identifier_map.lookup_default(child_ref->path.node_id,
317 missing_id);
318 if (new_node_id == missing_id) {
319 continue;
320 }
321 ref.path.node_id = new_node_id;
322 ref.path.id_in_node = child_ref->path.id_in_node;
323 }
324}
325
329static void node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
330{
331 ListBase anim_basepaths = {nullptr, nullptr};
332 Vector<bNode *> nodes_delayed_free;
333 const bNodeTree *ngroup = reinterpret_cast<const bNodeTree *>(gnode->id);
334
335 /* `wgroup` is a temporary copy of the #NodeTree we're merging in
336 * - All of wgroup's nodes are copied across to their new home.
337 * - `ngroup` (i.e. the source NodeTree) is left unscathed.
338 * - Temp copy. do change ID user-count for the copies.
339 */
340 bNodeTree *wgroup = bke::node_tree_copy_tree(bmain, *ngroup);
341
342 /* Add the nodes into the `ntree`. */
343 Vector<bNode *> new_nodes;
344 Map<int32_t, int32_t> node_identifier_map;
345 LISTBASE_FOREACH_MUTABLE (bNode *, node, &wgroup->nodes) {
346 new_nodes.append(node);
347 /* Remove interface nodes.
348 * This also removes remaining links to and from interface nodes.
349 */
350 if (node->is_group_input() || node->is_group_output()) {
351 /* We must delay removal since sockets will reference this node. see: #52092 */
352 nodes_delayed_free.append(node);
353 }
354
355 /* Keep track of this node's RNA "base" path (the part of the path identifying the node)
356 * if the old node-tree has animation data which potentially covers this node. */
357 std::optional<std::string> old_animation_basepath;
358 if (wgroup->adt) {
359 PointerRNA ptr = RNA_pointer_create_discrete(&wgroup->id, &RNA_Node, node);
360 old_animation_basepath = RNA_path_from_ID_to_struct(&ptr);
361 }
362
363 /* migrate node */
364 BLI_remlink(&wgroup->nodes, node);
365 BLI_addtail(&ntree->nodes, node);
366 const int32_t old_identifier = node->identifier;
367 bke::node_unique_id(*ntree, *node);
368 bke::node_unique_name(*ntree, *node);
369 node_identifier_map.add(old_identifier, node->identifier);
370
372
373 if (wgroup->adt) {
374 PointerRNA ptr = RNA_pointer_create_discrete(&ntree->id, &RNA_Node, node);
375 const std::optional<std::string> new_animation_basepath = RNA_path_from_ID_to_struct(&ptr);
376 BLI_addtail(&anim_basepaths,
377 animation_basepath_change_new(*old_animation_basepath, *new_animation_basepath));
378 }
379
380 node->location[0] += gnode->location[0];
381 node->location[1] += gnode->location[1];
382
383 node->flag |= NODE_SELECT;
384 }
385 wgroup->runtime->nodes_by_id.clear();
386
387 bNodeLink *glinks_first = (bNodeLink *)ntree->links.last;
388
389 /* Add internal links to the ntree */
390 LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &wgroup->links) {
391 BLI_remlink(&wgroup->links, link);
392 BLI_addtail(&ntree->links, link);
394 }
395
396 bNodeLink *glinks_last = (bNodeLink *)ntree->links.last;
397
398 /* and copy across the animation,
399 * note that the animation data's action can be nullptr here */
400 if (wgroup->adt) {
401 /* firstly, wgroup needs to temporary dummy action
402 * that can be destroyed, as it shares copies */
403 bAction *waction = reinterpret_cast<bAction *>(BKE_id_copy(bmain, &wgroup->adt->action->id));
404 const bool assign_ok = animrig::assign_action(waction, {wgroup->id, *wgroup->adt});
405 BLI_assert_msg(assign_ok, "assigning a copy of an already-assigned Action should work");
406 UNUSED_VARS_NDEBUG(assign_ok);
407
408 /* now perform the moving */
409 BKE_animdata_transfer_by_basepath(bmain, &wgroup->id, &ntree->id, &anim_basepaths);
410
411 /* paths + their wrappers need to be freed */
412 LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
413 animation_basepath_change_free(basepath_change);
414 }
415
416 /* free temp action too */
417 if (waction) {
418 const bool unassign_ok = animrig::unassign_action({wgroup->id, *wgroup->adt});
419 BLI_assert_msg(unassign_ok, "unassigning an Action that was just assigned should work");
420 UNUSED_VARS_NDEBUG(unassign_ok);
421 BKE_id_free(bmain, waction);
422 }
423 }
424
425 remap_pairing(*ntree, new_nodes, node_identifier_map);
426
427 /* free the group tree (takes care of user count) */
428 BKE_id_free(bmain, wgroup);
429
430 /* restore external links to and from the gnode */
431
432 /* input links */
433 if (glinks_first != nullptr) {
434 for (bNodeLink *link = glinks_first->next; link != glinks_last->next; link = link->next) {
435 if (link->fromnode->is_group_input()) {
436 const char *identifier = link->fromsock->identifier;
437 int num_external_links = 0;
438
439 /* find external links to this input */
440 for (bNodeLink *tlink = (bNodeLink *)ntree->links.first; tlink != glinks_first->next;
441 tlink = tlink->next)
442 {
443 if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) {
445 *ntree, *tlink->fromnode, *tlink->fromsock, *link->tonode, *link->tosock);
446 num_external_links++;
447 }
448 }
449
450 /* if group output is not externally linked,
451 * convert the constant input value to ensure somewhat consistent behavior */
452 if (num_external_links == 0) {
453 /* TODO */
454#if 0
455 bNodeSocket *sock = node_group_find_input_socket(gnode, identifier);
456 BLI_assert(sock);
457
458 nodeSocketCopy(
459 ntree, link->tosock->new_sock, link->tonode->new_node, ntree, sock, gnode);
460#endif
461 }
462 }
463 }
464
465 /* Also iterate over new links to cover passthrough links. */
466 glinks_last = (bNodeLink *)ntree->links.last;
467
468 /* output links */
469 for (bNodeLink *link = (bNodeLink *)ntree->links.first; link != glinks_first->next;
470 link = link->next)
471 {
472 if (link->fromnode == gnode) {
473 const char *identifier = link->fromsock->identifier;
474 int num_internal_links = 0;
475
476 /* find internal links to this output */
477 for (bNodeLink *tlink = glinks_first->next; tlink != glinks_last->next;
478 tlink = tlink->next)
479 {
480 /* only use active output node */
481 if (tlink->tonode->is_group_output() && (tlink->tonode->flag & NODE_DO_OUTPUT)) {
482 if (STREQ(tlink->tosock->identifier, identifier)) {
484 *ntree, *tlink->fromnode, *tlink->fromsock, *link->tonode, *link->tosock);
485 num_internal_links++;
486 }
487 }
488 }
489
490 /* if group output is not internally linked,
491 * convert the constant output value to ensure somewhat consistent behavior */
492 if (num_internal_links == 0) {
493 /* TODO */
494#if 0
495 bNodeSocket *sock = node_group_find_output_socket(gnode, identifier);
496 BLI_assert(sock);
497
498 nodeSocketCopy(ntree, link->tosock, link->tonode, ntree, sock, gnode);
499#endif
500 }
501 }
502 }
503 }
504
505 for (bNode *node : nodes_delayed_free) {
506 bke::node_remove_node(bmain, *ntree, *node, false);
507 }
508
509 update_nested_node_refs_after_ungroup(*ntree, *ngroup, *gnode, node_identifier_map);
510
511 /* delete the group instance and dereference group tree */
512 bke::node_remove_node(bmain, *ntree, *gnode, true);
513}
514
516{
517 Main *bmain = CTX_data_main(C);
518 SpaceNode *snode = CTX_wm_space_node(C);
519 const StringRef node_idname = node_group_idname(C);
520
522
523 Vector<bNode *> nodes_to_ungroup;
524 for (bNode *node : snode->edittree->all_nodes()) {
525 if (node->flag & NODE_SELECT) {
526 if (node->idname == node_idname) {
527 if (node->id != nullptr) {
528 nodes_to_ungroup.append(node);
529 }
530 }
531 }
532 }
533 if (nodes_to_ungroup.is_empty()) {
534 return OPERATOR_CANCELLED;
535 }
536 for (bNode *node : nodes_to_ungroup) {
537 node_group_ungroup(bmain, snode->edittree, node);
538 }
540 return OPERATOR_FINISHED;
541}
542
544{
545 /* identifiers */
546 ot->name = "Ungroup";
547 ot->description = "Ungroup selected nodes";
548 ot->idname = "NODE_OT_group_ungroup";
549
550 /* API callbacks. */
553
554 /* flags */
556}
557
559
560/* -------------------------------------------------------------------- */
563
568 Main &bmain, bNodeTree &ntree, bNodeTree &ngroup, const float2 &offset, const bool make_copy)
569{
570 node_deselect_all(ntree);
571
572 ListBase anim_basepaths = {nullptr, nullptr};
573
574 Map<bNode *, bNode *> node_map;
576 Map<int32_t, int32_t> node_identifier_map;
577
578 /* Add selected nodes into the ntree, ignoring interface nodes. */
579 VectorSet<bNode *> nodes_to_move = get_selected_nodes(ngroup);
580 nodes_to_move.remove_if(
581 [](const bNode *node) { return node->is_group_input() || node->is_group_output(); });
582
583 for (bNode *node : nodes_to_move) {
584 bNode *newnode;
585 if (make_copy) {
587 &ntree, *node, LIB_ID_COPY_DEFAULT, std::nullopt, std::nullopt, socket_map);
588 node_identifier_map.add(node->identifier, newnode->identifier);
589 }
590 else {
591 newnode = node;
592 BLI_remlink(&ngroup.nodes, newnode);
593 BLI_addtail(&ntree.nodes, newnode);
594 const int32_t old_identifier = node->identifier;
595 bke::node_unique_id(ntree, *newnode);
596 bke::node_unique_name(ntree, *newnode);
597 node_identifier_map.add(old_identifier, newnode->identifier);
598 }
599 node_map.add_new(node, newnode);
600
601 /* Keep track of this node's RNA "base" path (the part of the path identifying the node)
602 * if the old node-tree has animation data which potentially covers this node. */
603 if (ngroup.adt) {
604 PointerRNA ptr = RNA_pointer_create_discrete(&ngroup.id, &RNA_Node, newnode);
605 if (const std::optional<std::string> path = RNA_path_from_ID_to_struct(&ptr)) {
606 BLI_addtail(&anim_basepaths, animation_basepath_change_new(*path, *path));
607 }
608 }
609
610 /* ensure valid parent pointers, detach if parent stays inside the group */
611 if (newnode->parent && !(newnode->parent->flag & NODE_SELECT)) {
612 bke::node_detach_node(ngroup, *newnode);
613 }
614
615 newnode->location[0] += offset.x;
616 newnode->location[1] += offset.y;
617 }
618 if (!make_copy) {
620 }
621
622 /* add internal links to the ntree */
623 LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ngroup.links) {
624 const bool fromselect = (link->fromnode && nodes_to_move.contains(link->fromnode));
625 const bool toselect = (link->tonode && nodes_to_move.contains(link->tonode));
626
627 if (make_copy) {
628 /* make a copy of internal links */
629 if (fromselect && toselect) {
630 bke::node_add_link(ntree,
631 *node_map.lookup(link->fromnode),
632 *socket_map.lookup(link->fromsock),
633 *node_map.lookup(link->tonode),
634 *socket_map.lookup(link->tosock));
635 }
636 }
637 else {
638 /* move valid links over, delete broken links */
639 if (fromselect && toselect) {
640 BLI_remlink(&ngroup.links, link);
641 BLI_addtail(&ntree.links, link);
642 }
643 else if (fromselect || toselect) {
644 bke::node_remove_link(&ngroup, *link);
645 }
646 }
647 }
648
649 remap_pairing(ntree, nodes_to_move, node_identifier_map);
650
651 for (bNode *node : node_map.values()) {
652 bke::node_declaration_ensure(ntree, *node);
653 }
654
655 /* and copy across the animation,
656 * note that the animation data's action can be nullptr here */
657 if (ngroup.adt) {
658 /* now perform the moving */
659 BKE_animdata_transfer_by_basepath(&bmain, &ngroup.id, &ntree.id, &anim_basepaths);
660
661 /* paths + their wrappers need to be freed */
662 LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
663 animation_basepath_change_free(basepath_change);
664 }
665 }
666
668 if (!make_copy) {
670 }
671
672 return true;
673}
674
679
680/* Operator Property */
682 {NODE_GS_COPY, "COPY", 0, "Copy", "Copy to parent node tree, keep group intact"},
683 {NODE_GS_MOVE, "MOVE", 0, "Move", "Move to parent node tree, remove from group"},
684 {0, nullptr, 0, nullptr, nullptr},
685};
686
688{
689 Main *bmain = CTX_data_main(C);
690 ARegion *region = CTX_wm_region(C);
691 SpaceNode *snode = CTX_wm_space_node(C);
692 int type = RNA_enum_get(op->ptr, "type");
693
695
696 /* are we inside of a group? */
697 bNodeTree *ngroup = snode->edittree;
698 bNodeTree *nparent = ED_node_tree_get(snode, 1);
699 if (!nparent) {
700 BKE_report(op->reports, RPT_WARNING, "Not inside node group");
701 return OPERATOR_CANCELLED;
702 }
703 /* get node tree offset */
704 const float2 offset = space_node_group_offset(*snode);
705
706 switch (type) {
707 case NODE_GS_COPY:
708 if (!node_group_separate_selected(*bmain, *nparent, *ngroup, offset, true)) {
709 BKE_report(op->reports, RPT_WARNING, "Cannot separate nodes");
710 return OPERATOR_CANCELLED;
711 }
712 break;
713 case NODE_GS_MOVE:
714 if (!node_group_separate_selected(*bmain, *nparent, *ngroup, offset, false)) {
715 BKE_report(op->reports, RPT_WARNING, "Cannot separate nodes");
716 return OPERATOR_CANCELLED;
717 }
718 break;
719 }
720
721 /* switch to parent tree */
722 ED_node_tree_pop(region, snode);
723
725
726 return OPERATOR_FINISHED;
727}
728
730 wmOperator * /*op*/,
731 const wmEvent * /*event*/)
732{
734 C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Separate"), ICON_NONE);
735 uiLayout *layout = UI_popup_menu_layout(pup);
736
738 PointerRNA op_ptr = layout->op("NODE_OT_group_separate", IFACE_("Copy"), ICON_NONE);
739 RNA_enum_set(&op_ptr, "type", NODE_GS_COPY);
740 op_ptr = layout->op("NODE_OT_group_separate", IFACE_("Move"), ICON_NONE);
741 RNA_enum_set(&op_ptr, "type", NODE_GS_MOVE);
742
743 UI_popup_menu_end(C, pup);
744
745 return OPERATOR_INTERFACE;
746}
747
749{
750 /* identifiers */
751 ot->name = "Separate";
752 ot->description = "Separate selected nodes from the node group";
753 ot->idname = "NODE_OT_group_separate";
754
755 /* API callbacks. */
759
760 /* flags */
762
763 RNA_def_enum(ot->srna, "type", node_group_separate_types, NODE_GS_COPY, "Type", "");
764}
765
767
768/* -------------------------------------------------------------------- */
771
773{
774 VectorSet<bNode *> nodes_to_group = get_selected_nodes(node_tree);
775 nodes_to_group.remove_if(
776 [](bNode *node) { return node->is_group_input() || node->is_group_output(); });
777 nodes_to_group.remove(group_node);
778 return nodes_to_group;
779}
780
782 const VectorSet<bNode *> &nodes_to_group,
783 const StringRef ntree_idname,
784 ReportList &reports)
785{
786 if (nodes_to_group.is_empty()) {
787 return false;
788 }
789 /* make a local pseudo node tree to pass to the node poll functions */
790 bNodeTree *ngroup = bke::node_tree_add_tree(nullptr, "Pseudo Node Group", ntree_idname);
791 BLI_SCOPED_DEFER([&]() {
793 MEM_freeN(ngroup);
794 });
795
796 /* check poll functions for selected nodes */
797 for (bNode *node : nodes_to_group) {
798 const char *disabled_hint = nullptr;
799 if (node->typeinfo->poll_instance &&
800 !node->typeinfo->poll_instance(node, ngroup, &disabled_hint))
801 {
802 if (disabled_hint) {
803 BKE_reportf(&reports,
805 "Cannot add node '%s' in a group:\n %s",
806 node->name,
807 disabled_hint);
808 }
809 else {
810 BKE_reportf(&reports, RPT_WARNING, "Cannot add node '%s' in a group", node->name);
811 }
812 return false;
813 }
814 }
815
816 /* check if all connections are OK, no unselected node has both
817 * inputs and outputs to a selection */
818 ntree.ensure_topology_cache();
819 for (bNode *node : ntree.all_nodes()) {
820 if (nodes_to_group.contains(node)) {
821 continue;
822 }
823 auto sockets_connected_to_group = [&](const Span<bNodeSocket *> sockets) {
824 for (const bNodeSocket *socket : sockets) {
825 for (const bNodeSocket *other_socket : socket->directly_linked_sockets()) {
826 if (nodes_to_group.contains(const_cast<bNode *>(&other_socket->owner_node()))) {
827 return true;
828 }
829 }
830 }
831 return false;
832 };
833 if (sockets_connected_to_group(node->input_sockets()) &&
834 sockets_connected_to_group(node->output_sockets()))
835 {
836 return false;
837 }
838 }
839 /* Check if zone pairs are fully selected.
840 * Zone input or output nodes can only be grouped together with the paired node. */
841 for (const bke::bNodeZoneType *zone_type : bke::all_zone_types()) {
842 for (bNode *input_node : ntree.nodes_by_type(zone_type->input_idname)) {
843 if (bNode *output_node = zone_type->get_corresponding_output(ntree, *input_node)) {
844 const bool input_selected = nodes_to_group.contains(input_node);
845 const bool output_selected = nodes_to_group.contains(output_node);
846 if (input_selected && !output_selected) {
847 BKE_reportf(&reports,
849 "Cannot add zone input node '%s' to a group without its paired output '%s'",
850 input_node->name,
851 output_node->name);
852 return false;
853 }
854 if (output_selected && !input_selected) {
855 BKE_reportf(&reports,
857 "Cannot add zone output node '%s' to a group without its paired input '%s'",
858 output_node->name,
859 input_node->name);
860 return false;
861 }
862 }
863 }
864 }
865
866 return true;
867}
868
870 const bool use_size,
871 float2 &min,
872 float2 &max)
873{
874 if (nodes.is_empty()) {
875 min = float2(0);
876 max = float2(0);
877 return;
878 }
879
881 for (const bNode *node : nodes) {
882 float2 loc(node->location);
883 math::min_max(loc, min, max);
884 if (use_size) {
885 loc.x += node->width;
886 loc.y -= node->height;
887 math::min_max(loc, min, max);
888 }
889 }
890}
891
899 const bNodeSocket &socket)
900{
901 if (node_tree.has_available_link_cycle()) {
902 return socket;
903 }
904 const bNode &node = socket.owner_node();
905 if (!node.is_reroute()) {
906 return socket;
907 }
908 const bNodeSocket &other_socket = socket.in_out == SOCK_IN ? node.output_socket(0) :
909 node.input_socket(0);
910 if (!other_socket.is_logically_linked()) {
911 return socket;
912 }
913 return *other_socket.logically_linked_sockets().first();
914}
915
920static bool prefer_node_for_interface_name(const bNode &node)
921{
922 return node.is_group() || node.is_group_input() || node.is_group_output();
923}
924
926 bNodeTree &tree_for_interface,
927 const bNodeSocket &socket)
928{
929 /* The "example socket" has to have the same `in_out` status as the new interface socket. */
930 const bNodeSocket &socket_for_io = find_socket_to_use_for_interface(original_tree, socket);
931 const bNode &node_for_io = socket_for_io.owner_node();
932 const bNodeSocket &socket_for_name = prefer_node_for_interface_name(socket.owner_node()) ?
933 socket :
934 socket_for_io;
936 tree_for_interface, node_for_io, socket_for_io, socket_for_io.idname, socket_for_name.name);
937}
938
940 bNodeTree &ntree,
941 bNodeTree &group,
942 bNode &gnode,
943 const Map<int32_t, int32_t> &node_identifier_map)
944{
945 /* Update nested node references in the parent and child node tree. */
947 Vector<bNestedNodeRef> new_nested_node_refs;
948 /* Keep all nested node references that were in the group before. */
949 for (const bNestedNodeRef &ref : group.nested_node_refs_span()) {
950 new_nested_node_refs.append(ref);
951 }
952 Set<int32_t> used_nested_node_ref_ids;
953 for (const bNestedNodeRef &ref : group.nested_node_refs_span()) {
954 used_nested_node_ref_ids.add(ref.id);
955 }
956 Map<bNestedNodePath, int32_t> new_id_by_old_path;
957 for (bNestedNodeRef &ref : ntree.nested_node_refs_span()) {
958 const int32_t new_node_id = node_identifier_map.lookup_default(ref.path.node_id, -1);
959 if (new_node_id == -1) {
960 /* The node was not moved between node groups. */
961 continue;
962 }
963 bNestedNodeRef new_ref = ref;
964 new_ref.path.node_id = new_node_id;
965 /* Find new unique identifier for the nested node ref. */
966 while (true) {
967 const int32_t new_id = rng.get_int32(INT32_MAX);
968 if (used_nested_node_ref_ids.add(new_id)) {
969 new_ref.id = new_id;
970 break;
971 }
972 }
973 new_id_by_old_path.add_new(ref.path, new_ref.id);
974 new_nested_node_refs.append(new_ref);
975 /* Updated the nested node ref in the parent so that it points to the same node that is now
976 * inside of a nested group. */
977 ref.path.node_id = gnode.identifier;
978 ref.path.id_in_node = new_ref.id;
979 }
981 group.nested_node_refs = MEM_malloc_arrayN<bNestedNodeRef>(new_nested_node_refs.size(),
982 __func__);
984 new_nested_node_refs.data(), new_nested_node_refs.size(), group.nested_node_refs);
985 group.nested_node_refs_num = new_nested_node_refs.size();
986}
987
989 bNodeTree &ntree,
990 bNode *gnode,
991 const VectorSet<bNode *> &nodes_to_move)
992{
993 Main *bmain = CTX_data_main(&C);
994 bNodeTree &group = *reinterpret_cast<bNodeTree *>(gnode->id);
995 BLI_assert(!nodes_to_move.contains(gnode));
996
997 node_deselect_all(group);
998
999 float2 min, max;
1000 get_min_max_of_nodes(nodes_to_move, false, min, max);
1001 const float2 center = math::midpoint(min, max);
1002
1003 float2 real_min, real_max;
1004 get_min_max_of_nodes(nodes_to_move, true, real_min, real_max);
1005
1006 /* If only one node is selected expose all its sockets regardless of links. */
1007 const bool expose_visible = nodes_to_move.size() == 1;
1008
1009 /* Reuse an existing output node or create a new one. */
1010 group.ensure_topology_cache();
1011 bNode *output_node = [&]() {
1012 if (bNode *node = group.group_output_node()) {
1013 return node;
1014 }
1015 bNode *output_node = bke::node_add_static_node(&C, group, NODE_GROUP_OUTPUT);
1016 output_node->location[0] = real_max[0] - center[0] + 50.0f;
1017 return output_node;
1018 }();
1019
1020 /* Create new group input node for easier organization of the new nodes inside the group. */
1021 bNode *input_node = bke::node_add_static_node(&C, group, NODE_GROUP_INPUT);
1022 input_node->location[0] = real_min[0] - center[0] - 200.0f;
1023
1024 struct InputSocketInfo {
1025 /* The unselected node the original link came from. */
1026 bNode *from_node;
1027 /* All the links that came from the socket on the unselected node. */
1028 Vector<bNodeLink *> links;
1029 const bNodeTreeInterfaceSocket *interface_socket;
1030 };
1031
1032 struct OutputLinkInfo {
1033 bNodeLink *link;
1034 const bNodeTreeInterfaceSocket *interface_socket;
1035 };
1036
1037 struct NewInternalLinkInfo {
1038 bNode *node;
1039 bNodeSocket *socket;
1040 const bNodeTreeInterfaceSocket *interface_socket;
1041 };
1042
1043 /* Map from single non-selected output sockets to potentially many selected input sockets. */
1045 Vector<OutputLinkInfo> output_links;
1046 Set<bNodeLink *> internal_links_to_move;
1047 Set<bNodeLink *> links_to_remove;
1048 /* Map old to new node identifiers. */
1049 Map<int32_t, int32_t> node_identifier_map;
1050 Vector<NewInternalLinkInfo> new_internal_links;
1051
1052 ntree.ensure_topology_cache();
1053 /* Add all outputs first. */
1054 for (bNode *node : nodes_to_move) {
1055 for (bNodeSocket *output_socket : node->output_sockets()) {
1056 if (!output_socket->is_visible()) {
1057 for (bNodeLink *link : output_socket->directly_linked_links()) {
1058 links_to_remove.add(link);
1059 }
1060 continue;
1061 }
1062
1063 for (bNodeLink *link : output_socket->directly_linked_links()) {
1064 if (bke::node_link_is_hidden(*link)) {
1065 links_to_remove.add(link);
1066 continue;
1067 }
1068 if (link->tonode == gnode) {
1069 links_to_remove.add(link);
1070 continue;
1071 }
1072 if (nodes_to_move.contains(link->tonode)) {
1073 internal_links_to_move.add(link);
1074 continue;
1075 }
1077 ntree, group, *link->fromsock);
1078 if (io_socket) {
1079 output_links.append({link, io_socket});
1080 }
1081 else {
1082 links_to_remove.add(link);
1083 }
1084 }
1085 if (expose_visible && !output_socket->is_directly_linked()) {
1087 group, *node, *output_socket);
1088 if (io_socket) {
1089 new_internal_links.append({node, output_socket, io_socket});
1090 }
1091 }
1092 }
1093 }
1094 /* Now add all inputs. */
1095 for (bNode *node : nodes_to_move) {
1096 for (bNodeSocket *input_socket : node->input_sockets()) {
1097 if (!input_socket->is_visible()) {
1098 for (bNodeLink *link : input_socket->directly_linked_links()) {
1099 links_to_remove.add(link);
1100 }
1101 continue;
1102 }
1103
1104 for (bNodeLink *link : input_socket->directly_linked_links()) {
1105 if (bke::node_link_is_hidden(*link)) {
1106 links_to_remove.add(link);
1107 continue;
1108 }
1109 if (link->fromnode == gnode) {
1110 links_to_remove.add(link);
1111 continue;
1112 }
1113 if (nodes_to_move.contains(link->fromnode)) {
1114 internal_links_to_move.add(link);
1115 continue;
1116 }
1117 InputSocketInfo &info = input_links.lookup_or_add_default(link->fromsock);
1118 info.from_node = link->fromnode;
1119 info.links.append(link);
1120 if (!info.interface_socket) {
1121 info.interface_socket = add_interface_from_socket(ntree, group, *link->tosock);
1122 }
1123 }
1124 if (expose_visible && !input_socket->is_directly_linked()) {
1126 group, *node, *input_socket);
1127 if (io_socket) {
1128 new_internal_links.append({node, input_socket, io_socket});
1129 }
1130 }
1131 }
1132 }
1133
1134 /* Un-parent nodes when only the parent or child moves into the group. */
1135 for (bNode *node : ntree.all_nodes()) {
1136 if (node->parent && nodes_to_move.contains(node->parent) && !nodes_to_move.contains(node)) {
1137 bke::node_detach_node(ntree, *node);
1138 }
1139 }
1140 for (bNode *node : nodes_to_move) {
1141 if (node->parent && !nodes_to_move.contains(node->parent)) {
1142 bke::node_detach_node(ntree, *node);
1143 }
1144 }
1145
1146 /* Move animation data from the parent tree to the group. */
1147 if (ntree.adt) {
1148 ListBase anim_basepaths = {nullptr, nullptr};
1149 for (bNode *node : nodes_to_move) {
1150 PointerRNA ptr = RNA_pointer_create_discrete(&ntree.id, &RNA_Node, node);
1151 if (const std::optional<std::string> path = RNA_path_from_ID_to_struct(&ptr)) {
1152 BLI_addtail(&anim_basepaths, animation_basepath_change_new(*path, *path));
1153 }
1154 }
1155 BKE_animdata_transfer_by_basepath(bmain, &ntree.id, &group.id, &anim_basepaths);
1156
1157 LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
1158 animation_basepath_change_free(basepath_change);
1159 }
1160 }
1161
1162 /* Move nodes into the group. */
1163 for (bNode *node : nodes_to_move) {
1164 const int32_t old_identifier = node->identifier;
1165
1166 BLI_remlink(&ntree.nodes, node);
1167 BLI_addtail(&group.nodes, node);
1168 bke::node_unique_id(group, *node);
1169 bke::node_unique_name(group, *node);
1170
1171 node_identifier_map.add(old_identifier, node->identifier);
1172
1174 BKE_ntree_update_tag_node_new(&group, node);
1175 }
1177
1178 /* Update input and output node first, since the group node declaration can depend on them. */
1180 nodes::update_node_declaration_and_sockets(group, *output_node);
1181
1182 /* move nodes in the group to the center */
1183 for (bNode *node : nodes_to_move) {
1184 node->location[0] -= center[0];
1185 node->location[1] -= center[1];
1186 }
1187
1188 for (bNodeLink *link : internal_links_to_move) {
1189 BLI_remlink(&ntree.links, link);
1190 BLI_addtail(&group.links, link);
1192 BKE_ntree_update_tag_link_added(&group, link);
1193 }
1194
1195 for (bNodeLink *link : links_to_remove) {
1196 bke::node_remove_link(&ntree, *link);
1197 }
1198
1199 /* Handle links to the new group inputs. */
1200 for (const auto item : input_links.items()) {
1201 const StringRefNull interface_identifier = item.value.interface_socket->identifier;
1202 bNodeSocket *input_socket = node_group_input_find_socket(input_node, interface_identifier);
1203
1204 for (bNodeLink *link : item.value.links) {
1205 /* Move the link into the new group, connected from the input node to the original socket. */
1206 BLI_remlink(&ntree.links, link);
1207 BLI_addtail(&group.links, link);
1209 BKE_ntree_update_tag_link_added(&group, link);
1210 link->fromnode = input_node;
1211 link->fromsock = input_socket;
1212 }
1213 }
1214
1215 /* Handle links to new group outputs. */
1216 for (const OutputLinkInfo &info : output_links) {
1217 /* Create a new link inside of the group. */
1218 const StringRefNull io_identifier = info.interface_socket->identifier;
1219 bNodeSocket *output_sock = node_group_output_find_socket(output_node, io_identifier);
1221 group, *info.link->fromnode, *info.link->fromsock, *output_node, *output_sock);
1222 }
1223
1224 /* Handle new links inside the group. */
1225 for (const NewInternalLinkInfo &info : new_internal_links) {
1226 const StringRefNull io_identifier = info.interface_socket->identifier;
1227 if (info.socket->in_out == SOCK_IN) {
1228 bNodeSocket *input_socket = node_group_input_find_socket(input_node, io_identifier);
1229 bke::node_add_link(group, *input_node, *input_socket, *info.node, *info.socket);
1230 }
1231 else {
1232 bNodeSocket *output_socket = node_group_output_find_socket(output_node, io_identifier);
1233 bke::node_add_link(group, *info.node, *info.socket, *output_node, *output_socket);
1234 }
1235 }
1236
1237 remap_pairing(group, nodes_to_move, node_identifier_map);
1238
1239 if (group.type == NTREE_GEOMETRY) {
1241 }
1242
1243 if (ELEM(group.type, NTREE_GEOMETRY, NTREE_COMPOSIT)) {
1245 }
1246
1248
1249 /* Add new links to inputs outside of the group. */
1250 for (const auto item : input_links.items()) {
1251 const StringRefNull interface_identifier = item.value.interface_socket->identifier;
1252 bNodeSocket *group_node_socket = node_group_find_input_socket(gnode, interface_identifier);
1253 bke::node_add_link(ntree, *item.value.from_node, *item.key, *gnode, *group_node_socket);
1254 }
1255
1256 /* Add new links to outputs outside the group. */
1257 for (const OutputLinkInfo &info : output_links) {
1258 /* Reconnect the link to the group node instead of the node now inside the group. */
1259 info.link->fromnode = gnode;
1260 info.link->fromsock = node_group_find_output_socket(gnode, info.interface_socket->identifier);
1261 }
1262
1263 update_nested_node_refs_after_moving_nodes_into_group(ntree, group, *gnode, node_identifier_map);
1264
1266}
1267
1269 bNodeTree &ntree,
1270 const VectorSet<bNode *> &nodes_to_group,
1271 const StringRef ntype,
1272 const StringRef ntreetype)
1273{
1274 Main *bmain = CTX_data_main(&C);
1275
1276 float2 min, max;
1277 get_min_max_of_nodes(nodes_to_group, false, min, max);
1278
1279 /* New node-tree. */
1280 bNodeTree *ngroup = bke::node_tree_add_tree(bmain, "NodeGroup", ntreetype);
1281
1282 BKE_id_move_to_same_lib(*bmain, ngroup->id, ntree.id);
1283
1284 /* make group node */
1285 bNode *gnode = bke::node_add_node(&C, ntree, ntype);
1286 gnode->id = (ID *)ngroup;
1287
1288 gnode->location[0] = 0.5f * (min[0] + max[0]);
1289 gnode->location[1] = 0.5f * (min[1] + max[1]);
1290
1291 node_group_make_insert_selected(C, ntree, gnode, nodes_to_group);
1292
1293 return gnode;
1294}
1295
1297 int num_inputs = 0;
1303
1304 bNodeSocket *get_new_input(const bNodeSocket *old_socket, bNode &new_node) const
1305 {
1306 if (const std::optional<int> index = new_index_by_src_socket.lookup_try(old_socket)) {
1307 return &new_node.input_socket(*index);
1308 }
1309 return nullptr;
1310 }
1311
1312 bNodeSocket *get_new_output(const bNodeSocket *old_socket, bNode &new_node) const
1313 {
1314 if (const std::optional<int> index = new_index_by_src_socket.lookup_try(old_socket)) {
1315 return &new_node.output_socket(*index);
1316 }
1317 return nullptr;
1318 }
1319};
1320
1322 bNodeTree &group,
1323 const bNode &src_node,
1324 const nodes::ItemDeclaration &item_decl,
1326 WrapperNodeGroupMapping &r_mapping)
1327{
1328 if (const nodes::SocketDeclaration *socket_decl = dynamic_cast<const nodes::SocketDeclaration *>(
1329 &item_decl))
1330 {
1331 const bNodeSocket &socket = src_node.socket_by_decl(*socket_decl);
1332 if (!socket.is_available()) {
1333 return;
1334 }
1336 group, src_node, socket);
1337 if (!io_socket) {
1338 return;
1339 }
1340 group.tree_interface.move_item_to_parent(io_socket->item, parent, INT32_MAX);
1341 if (socket.is_input()) {
1342 r_mapping.new_index_by_src_socket.add_new(&socket, r_mapping.num_inputs++);
1343 r_mapping.exposed_input_indices.append(socket.index());
1344 }
1345 else {
1346 r_mapping.new_index_by_src_socket.add_new(&socket, r_mapping.num_outputs++);
1347 r_mapping.exposed_output_indices.append(socket.index());
1348 }
1349 }
1350 else if (const nodes::PanelDeclaration *panel_decl =
1351 dynamic_cast<const nodes::PanelDeclaration *>(&item_decl))
1352 {
1354 if (panel_decl->default_collapsed) {
1356 }
1357 bNodeTreeInterfacePanel *io_panel = group.tree_interface.add_panel(
1358 panel_decl->name, panel_decl->description, flag, parent);
1359 r_mapping.new_by_old_panel_identifier.add_new(panel_decl->identifier, io_panel->identifier);
1360 for (const nodes::ItemDeclaration *child_item_decl : panel_decl->items) {
1362 group, src_node, *child_item_decl, io_panel, r_mapping);
1363 }
1364 }
1365}
1366
1368 const bNodeTree &src_tree,
1369 const bNode &src_node,
1370 WrapperNodeGroupMapping &r_mapping)
1371{
1372 Main &bmain = *CTX_data_main(&C);
1373
1375 &bmain, bke::node_label(src_tree, src_node), src_tree.idname);
1376 dst_group->color_tag = int(bke::node_color_tag(src_node));
1377
1378 const nodes::NodeDeclaration &node_decl = *src_node.declaration();
1379 for (const nodes::ItemDeclaration *item_decl : node_decl.root_items) {
1381 *dst_group, src_node, *item_decl, nullptr, r_mapping);
1382 }
1383
1384 /* Add the node that make up the wrapper node group. */
1385 bNode &input_node = *bke::node_add_static_node(&C, *dst_group, NODE_GROUP_INPUT);
1386 bNode &output_node = *bke::node_add_static_node(&C, *dst_group, NODE_GROUP_OUTPUT);
1387
1388 Map<const bNodeSocket *, bNodeSocket *> inner_node_socket_mapping;
1389 bNode &inner_node = *bke::node_copy_with_mapping(
1390 dst_group, src_node, 0, std::nullopt, std::nullopt, inner_node_socket_mapping);
1391
1392 /* Position nodes. */
1393 input_node.location[0] = -300 - input_node.width;
1394 output_node.location[0] = 300;
1395 inner_node.location[0] = -src_node.width / 2;
1396 inner_node.location[1] = 0;
1397 inner_node.width = src_node.width;
1398 inner_node.parent = nullptr;
1399
1400 /* This makes sure that all nodes have the correct sockets so that we can link. */
1401 BKE_main_ensure_invariants(bmain, dst_group->id);
1402
1403 /* Expand all panels in wrapper node group. */
1404 for (bNodePanelState &panel_state : inner_node.panel_states()) {
1405 panel_state.flag &= ~NODE_PANEL_COLLAPSED;
1406 }
1407 /* Make all sockets visible in wrapper node group. */
1408 for (bNodeSocket *socket : inner_node.input_sockets()) {
1409 socket->flag &= ~SOCK_HIDDEN;
1410 }
1411 for (bNodeSocket *socket : inner_node.output_sockets()) {
1412 socket->flag &= ~SOCK_HIDDEN;
1413 }
1414
1415 const Array<bNodeSocket *> group_inputs = input_node.output_sockets().drop_back(1);
1416 const Array<bNodeSocket *> group_outputs = output_node.input_sockets().drop_back(1);
1417 const Array<bNodeSocket *> inner_inputs = inner_node.input_sockets();
1418 const Array<bNodeSocket *> inner_outputs = inner_node.output_sockets();
1419 BLI_assert(group_inputs.size() == r_mapping.exposed_input_indices.size());
1420 BLI_assert(group_outputs.size() == r_mapping.exposed_output_indices.size());
1421
1422 /* Add links. */
1423 for (const int i : group_inputs.index_range()) {
1424 bke::node_add_link(*dst_group,
1425 input_node,
1426 *group_inputs[i],
1427 inner_node,
1428 *inner_inputs[r_mapping.exposed_input_indices[i]]);
1429 }
1430 for (const int i : group_outputs.index_range()) {
1431 bke::node_add_link(*dst_group,
1432 inner_node,
1433 *inner_outputs[r_mapping.exposed_output_indices[i]],
1434 output_node,
1435 *group_outputs[i]);
1436 }
1437
1438 BKE_main_ensure_invariants(bmain, dst_group->id);
1439 return dst_group;
1440}
1441
1443 bNodeTree &ntree,
1444 bNode &src_node,
1445 const StringRef node_idname)
1446{
1447 Main &bmain = *CTX_data_main(&C);
1448
1450 bNodeTree *wrapper_group = node_group_make_wrapper(C, ntree, src_node, mapping);
1451
1452 /* Create a group node. */
1453 bNode *gnode = bke::node_add_node(&C, ntree, node_idname);
1454 STRNCPY_UTF8(gnode->name, BKE_id_name(wrapper_group->id));
1455 bke::node_unique_name(ntree, *gnode);
1456
1457 /* Assign the newly created wrapper group to the new group node. */
1458 gnode->id = &wrapper_group->id;
1459
1460 /* Position node exactly where the old node was. */
1461 gnode->parent = src_node.parent;
1462 gnode->width = std::max<float>(src_node.width, GROUP_NODE_MIN_WIDTH);
1463 copy_v2_v2(gnode->location, src_node.location);
1464
1466 ntree.ensure_topology_cache();
1467
1468 /* Keep old socket visibility. */
1469 for (const bNodeSocket *src_socket : src_node.input_sockets()) {
1470 if (bNodeSocket *new_socket = mapping.get_new_input(src_socket, *gnode)) {
1471 new_socket->flag |= src_socket->flag & (SOCK_HIDDEN | SOCK_COLLAPSED);
1472 }
1473 }
1474 for (const bNodeSocket *src_socket : src_node.output_sockets()) {
1475 if (bNodeSocket *new_socket = mapping.get_new_output(src_socket, *gnode)) {
1476 new_socket->flag |= src_socket->flag & (SOCK_HIDDEN | SOCK_COLLAPSED);
1477 }
1478 }
1479
1480 /* Keep old panel collapse status. */
1481 const Span<bNodePanelState> src_panel_states = src_node.panel_states();
1482 MutableSpan<bNodePanelState> new_panel_states = gnode->panel_states();
1483 for (const bNodePanelState &src_panel_state : src_panel_states) {
1484 if (const std::optional<int> new_identifier = mapping.new_by_old_panel_identifier.lookup_try(
1485 src_panel_state.identifier))
1486 {
1487 for (bNodePanelState &new_panel_state : new_panel_states) {
1488 if (new_panel_state.identifier == *new_identifier) {
1489 SET_FLAG_FROM_TEST(new_panel_state.flag,
1490 src_panel_state.flag & NODE_PANEL_COLLAPSED,
1492 }
1493 }
1494 }
1495 }
1496
1497 /* Relink links from old to new node. */
1498 LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree.links) {
1499 if (link->tonode == &src_node) {
1500 if (bNodeSocket *new_to_socket = mapping.get_new_input(link->tosock, *gnode)) {
1501 link->tonode = gnode;
1502 link->tosock = new_to_socket;
1503 continue;
1504 }
1505 bke::node_remove_link(&ntree, *link);
1506 continue;
1507 }
1508 if (link->fromnode == &src_node) {
1509 if (bNodeSocket *new_from_socket = mapping.get_new_output(link->fromsock, *gnode)) {
1510 link->fromnode = gnode;
1511 link->fromsock = new_from_socket;
1512 continue;
1513 }
1514 bke::node_remove_link(&ntree, *link);
1515 continue;
1516 }
1517 }
1518
1519 /* Remove the old node because it has been replaced. Use the name of the removed node for the new
1520 * group node. This also keeps animation data working. */
1521 std::string old_node_name = src_node.name;
1522 bke::node_remove_node(&bmain, ntree, src_node, true, false);
1523 STRNCPY(gnode->name, old_node_name.c_str());
1524
1527 return gnode;
1528}
1529
1531{
1532 ARegion &region = *CTX_wm_region(C);
1533 SpaceNode &snode = *CTX_wm_space_node(C);
1534 bNodeTree &ntree = *snode.edittree;
1535 const StringRef ntree_idname = group_ntree_idname(C);
1536 const StringRef node_idname = node_group_idname(C);
1537 Main *bmain = CTX_data_main(C);
1538
1540
1541 VectorSet<bNode *> nodes_to_group = get_nodes_to_group(ntree, nullptr);
1542 if (!node_group_make_test_selected(ntree, nodes_to_group, ntree_idname, *op->reports)) {
1543 return OPERATOR_CANCELLED;
1544 }
1545
1546 bNode *gnode = nullptr;
1547 if (nodes_to_group.size() == 1 && nodes_to_group[0]->declaration()) {
1548 gnode = node_group_make_from_node_declaration(*C, ntree, *nodes_to_group[0], node_idname);
1549 }
1550 else {
1551 gnode = node_group_make_from_nodes(*C, ntree, nodes_to_group, node_idname, ntree_idname);
1552 }
1553
1554 if (gnode) {
1555 bNodeTree *ngroup = (bNodeTree *)gnode->id;
1556
1557 bke::node_set_active(ntree, *gnode);
1558 if (ngroup) {
1559 ED_node_tree_push(&region, &snode, ngroup, gnode);
1560 }
1561 }
1562
1564
1565 /* We broke relations in node tree, need to rebuild them in the graphs. */
1567
1568 return OPERATOR_FINISHED;
1569}
1570
1572{
1573 /* identifiers */
1574 ot->name = "Make Group";
1575 ot->description = "Make group from selected nodes";
1576 ot->idname = "NODE_OT_group_make";
1577
1578 /* API callbacks. */
1579 ot->exec = node_group_make_exec;
1581
1582 /* flags */
1583 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1584}
1585
1587
1588/* -------------------------------------------------------------------- */
1591
1593{
1594 SpaceNode *snode = CTX_wm_space_node(C);
1595 ARegion *region = CTX_wm_region(C);
1596 bNodeTree *ntree = snode->edittree;
1597 const StringRef node_idname = node_group_idname(C);
1598
1600
1601 bNode *gnode = node_group_get_active(C, node_idname);
1602 if (!gnode || !gnode->id) {
1603 return OPERATOR_CANCELLED;
1604 }
1605
1606 bNodeTree *ngroup = reinterpret_cast<bNodeTree *>(gnode->id);
1607 VectorSet<bNode *> nodes_to_group = get_nodes_to_group(*ntree, gnode);
1608
1609 /* Make sure that there won't be a node group containing itself afterwards. */
1610 for (const bNode *group : nodes_to_group) {
1611 if (!group->is_group() || group->id == nullptr) {
1612 continue;
1613 }
1614 if (bke::node_tree_contains_tree(*reinterpret_cast<bNodeTree *>(group->id), *ngroup)) {
1616 op->reports, RPT_WARNING, "Cannot insert group '%s' in '%s'", group->name, gnode->name);
1617 return OPERATOR_CANCELLED;
1618 }
1619 }
1620
1621 if (!node_group_make_test_selected(*ntree, nodes_to_group, ngroup->idname, *op->reports)) {
1622 return OPERATOR_CANCELLED;
1623 }
1624
1625 node_group_make_insert_selected(*C, *ntree, gnode, nodes_to_group);
1626
1627 bke::node_set_active(*ntree, *gnode);
1628 ED_node_tree_push(region, snode, ngroup, gnode);
1629
1630 return OPERATOR_FINISHED;
1631}
1632
1634{
1635 /* identifiers */
1636 ot->name = "Group Insert";
1637 ot->description = "Insert selected nodes into a node group";
1638 ot->idname = "NODE_OT_group_insert";
1639
1640 /* API callbacks. */
1641 ot->exec = node_group_insert_exec;
1643
1644 /* flags */
1645 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1646}
1647
1649
1650/* -------------------------------------------------------------------- */
1653
1655{
1656 SpaceNode *snode = CTX_wm_space_node(C);
1657 if (!snode) {
1658 return false;
1659 }
1660 bNodeTree *ntree = snode->edittree;
1661 if (!ntree) {
1662 return false;
1663 }
1664 if (!ID_IS_EDITABLE(ntree)) {
1665 return false;
1666 }
1667 if (snode->nodetree == snode->edittree) {
1668 /* Top-level node group does not have enough context to set the node width. */
1669 CTX_wm_operator_poll_msg_set(C, "There is no parent group node in this context");
1670 return false;
1671 }
1672 return true;
1673}
1674
1676{
1677 SpaceNode *snode = CTX_wm_space_node(C);
1678 bNodeTree *ntree = snode->edittree;
1679
1680 bNodeTreePath *last_path_item = static_cast<bNodeTreePath *>(snode->treepath.last);
1681 bNodeTreePath *parent_path_item = last_path_item->prev;
1682 if (!parent_path_item) {
1683 return OPERATOR_CANCELLED;
1684 }
1685 bNodeTree *parent_ntree = parent_path_item->nodetree;
1686 if (!parent_ntree) {
1687 return OPERATOR_CANCELLED;
1688 }
1689 parent_ntree->ensure_topology_cache();
1690 bNode *parent_node = bke::node_find_node_by_name(*parent_ntree, last_path_item->node_name);
1691 if (!parent_node) {
1692 return OPERATOR_CANCELLED;
1693 }
1694 ntree->default_group_node_width = parent_node->width;
1696 return OPERATOR_CANCELLED;
1697}
1698
1700{
1701 /* identifiers */
1702 ot->name = "Set Default Group Node Width";
1703 ot->description = "Set the width based on the parent group node in the current context";
1704 ot->idname = "NODE_OT_default_group_width_set";
1705
1706 /* API callbacks. */
1709
1710 /* flags */
1711 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1712}
1713
1715
1716} // namespace blender::ed::space_node
Functions and classes to work with Actions.
Blender kernel action and pose functionality.
void BKE_animdata_transfer_by_basepath(struct Main *bmain, struct ID *srcID, struct ID *dstID, struct ListBase *basepaths)
Definition anim_data.cc:611
SpaceNode * CTX_wm_space_node(const bContext *C)
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
void BKE_id_free(Main *bmain, void *idv)
void BKE_id_move_to_same_lib(Main &bmain, ID &id, const ID &owner_id)
Definition lib_id.cc:879
ID * BKE_id_copy(Main *bmain, const ID *id)
Definition lib_id.cc:782
@ LIB_ID_COPY_DEFAULT
const char * BKE_id_name(const ID &id)
void BKE_main_ensure_invariants(Main &bmain, std::optional< blender::Span< ID * > > modified_ids=std::nullopt)
constexpr int GROUP_NODE_MIN_WIDTH
Definition BKE_node.hh:1253
#define NODE_GROUP_OUTPUT
Definition BKE_node.hh:815
#define NODE_GROUP_INPUT
Definition BKE_node.hh:814
void BKE_ntree_update_tag_node_new(bNodeTree *ntree, bNode *node)
void BKE_ntree_update_tag_all(bNodeTree *ntree)
void BKE_ntree_update_tag_link_removed(bNodeTree *ntree)
void BKE_ntree_update_tag_node_removed(bNodeTree *ntree)
void BKE_ntree_update_tag_node_property(bNodeTree *ntree, bNode *node)
void BKE_ntree_update_tag_link_added(bNodeTree *ntree, bNodeLink *link)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
MINLINE void copy_v2_v2(float r[2], const float a[2])
#define BLI_SCOPED_DEFER(function_to_defer)
#define STR_ELEM(...)
Definition BLI_string.h:661
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define STRNCPY_UTF8(dst, src)
#define INIT_MINMAX2(min, max)
#define UNUSED_VARS_NDEBUG(...)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define AT
#define STREQ(a, b)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
void DEG_relations_tag_update(Main *bmain)
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:705
#define ID_MISSING(_id)
Definition DNA_ID.h:692
@ NODE_INTERFACE_PANEL_DEFAULT_CLOSED
@ NODE_DO_OUTPUT
@ NODE_SELECT
@ NTREE_GEOMETRY
@ NTREE_COMPOSIT
@ SOCK_IN
@ SOCK_COLLAPSED
@ SOCK_HIDDEN
@ NODE_PANEL_COLLAPSED
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_FINISHED
@ OPERATOR_PASS_THROUGH
bool ED_node_is_geometry(const SpaceNode *snode)
Definition node_edit.cc:502
bNodeTree * ED_node_tree_get(SpaceNode *snode, int level)
bool ED_node_is_compositor(const SpaceNode *snode)
Definition node_edit.cc:487
void ED_node_tree_pop(ARegion *region, SpaceNode *snode)
void ED_node_tree_push(ARegion *region, SpaceNode *snode, bNodeTree *ntree, bNode *gnode)
bool ED_node_is_shader(SpaceNode *snode)
Definition node_edit.cc:492
bool ED_node_is_texture(SpaceNode *snode)
Definition node_edit.cc:497
void ED_preview_kill_jobs(wmWindowManager *wm, Main *bmain)
bool ED_operator_node_editable(bContext *C)
bool ED_operator_node_active(bContext *C)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
struct blender::bke::bNodeTreeType * ntreeType_Shader
struct blender::bke::bNodeTreeType * ntreeType_Texture
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
#define C
Definition RandGen.cpp:29
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
uiBut * UI_but_find_mouse_over(const ARegion *region, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1668
#define NC_NODE
Definition WM_types.hh:394
#define NC_SCENE
Definition WM_types.hh:378
#define NA_ADDED
Definition WM_types.hh:586
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_NODES
Definition WM_types.hh:436
#define NA_EDITED
Definition WM_types.hh:584
#define ND_NODE_GIZMO
Definition WM_types.hh:516
std::optional< Value > lookup_try(const Key &key) const
Definition BLI_map.hh:531
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
int64_t size() const
Definition BLI_array.hh:256
IndexRange index_range() const
Definition BLI_array.hh:360
Value & lookup_or_add_default(const Key &key)
Definition BLI_map.hh:639
ValueIterator values() const &
Definition BLI_map.hh:884
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
ItemIterator items() const &
Definition BLI_map.hh:902
static RandomNumberGenerator from_random_seed()
Definition rand.cc:288
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr int64_t size() const
constexpr const char * data() const
bool remove(const Key &key)
int64_t size() const
bool contains(const Key &key) const
int64_t remove_if(Predicate &&predicate)
int64_t size() const
void append(const T &value)
bool is_empty() const
virtual const int & get_corresponding_output_id(const bNode &input_bnode) const =0
Vector< ItemDeclaration * > root_items
#define INT32_MAX
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
bool unassign_action(ID &animated_id)
bool assign_action(bAction *action, ID &animated_id)
bNodeTreeInterfaceSocket * add_interface_socket_from_node(bNodeTree &ntree, const bNode &from_node, const bNodeSocket &from_sock, StringRef socket_type, StringRef name)
bNode * node_find_node_by_name(bNodeTree &ntree, StringRefNull name)
Definition node.cc:3275
NodeColorTag node_color_tag(const bNode &node)
Definition node.cc:5019
const bNodeZoneType * zone_type_by_node_type(const int node_type)
std::string node_label(const bNodeTree &ntree, const bNode &node)
Definition node.cc:4970
void node_tree_free_tree(bNodeTree &ntree)
Definition node.cc:4455
bNodeTree * node_tree_copy_tree(Main *bmain, const bNodeTree &ntree)
Definition node.cc:4114
void node_remove_node(Main *bmain, bNodeTree &ntree, bNode &node, bool do_id_user, bool remove_animation=true)
Definition node.cc:4386
bNode * node_add_node(const bContext *C, bNodeTree &ntree, StringRef idname, std::optional< int > unique_identifier=std::nullopt)
Definition node.cc:3477
bNode * node_get_active(bNodeTree &ntree)
Definition node.cc:4685
void node_unique_id(bNodeTree &ntree, bNode &node)
Definition node.cc:3459
void node_remove_link(bNodeTree *ntree, bNodeLink &link)
Definition node.cc:3847
Span< const bNodeZoneType * > all_zone_types()
bool node_tree_contains_tree(const bNodeTree &tree_to_search_in, const bNodeTree &tree_to_search_for)
Definition node.cc:4663
void node_detach_node(bNodeTree &ntree, bNode &node)
Definition node.cc:3986
void node_rebuild_id_vector(bNodeTree &node_tree)
Definition node.cc:4291
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
Span< int > all_zone_input_node_types()
bNodeTree * node_tree_add_tree(Main *bmain, StringRef name, StringRef idname)
Definition node.cc:4085
bNode * node_copy_with_mapping(bNodeTree *dst_tree, const bNode &node_src, int flag, std::optional< StringRefNull > dst_unique_name, std::optional< int > dst_unique_identifier, Map< const bNodeSocket *, bNodeSocket * > &new_socket_map, bool allow_duplicate_names=false)
Definition node.cc:3553
bool node_declaration_ensure(bNodeTree &ntree, bNode &node)
Definition node.cc:4818
void node_set_active(bNodeTree &ntree, bNode &node)
Definition node.cc:4724
void node_unique_name(bNodeTree &ntree, bNode &node)
Definition node.cc:3453
bool node_link_is_hidden(const bNodeLink &link)
Definition node.cc:3882
static bool node_default_group_width_set_poll(bContext *C)
static bNode * node_group_make_from_node_declaration(bContext &C, bNodeTree &ntree, bNode &src_node, const StringRef node_idname)
static bool prefer_node_for_interface_name(const bNode &node)
static void add_node_group_interface_from_declaration_recursive(bNodeTree &group, const bNode &src_node, const nodes::ItemDeclaration &item_decl, bNodeTreeInterfacePanel *parent, WrapperNodeGroupMapping &r_mapping)
static bool node_group_operator_active_poll(bContext *C)
Definition node_group.cc:75
static bNodeTreeInterfaceSocket * add_interface_from_socket(const bNodeTree &original_tree, bNodeTree &tree_for_interface, const bNodeSocket &socket)
static AnimationBasePathChange * animation_basepath_change_new(const StringRef src_basepath, const StringRef dst_basepath)
static VectorSet< bNode * > get_nodes_to_group(bNodeTree &node_tree, bNode *group_node)
static wmOperatorStatus node_group_insert_exec(bContext *C, wmOperator *op)
static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, bNode *gnode, const VectorSet< bNode * > &nodes_to_move)
static wmOperatorStatus node_default_group_width_set_exec(bContext *C, wmOperator *)
bool node_deselect_all(bNodeTree &node_tree)
static void node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
static wmOperatorStatus node_group_separate_invoke(bContext *C, wmOperator *, const wmEvent *)
static bool node_group_make_test_selected(bNodeTree &ntree, const VectorSet< bNode * > &nodes_to_group, const StringRef ntree_idname, ReportList &reports)
void NODE_OT_group_make(wmOperatorType *ot)
static void update_nested_node_refs_after_moving_nodes_into_group(bNodeTree &ntree, bNodeTree &group, bNode &gnode, const Map< int32_t, int32_t > &node_identifier_map)
static bNode * node_group_get_active(bContext *C, const StringRef node_idname)
static const EnumPropertyItem node_group_separate_types[]
void NODE_OT_group_insert(wmOperatorType *ot)
static bool node_group_separate_selected(Main &bmain, bNodeTree &ntree, bNodeTree &ngroup, const float2 &offset, const bool make_copy)
static wmOperatorStatus node_group_ungroup_exec(bContext *C, wmOperator *)
static const bNodeSocket & find_socket_to_use_for_interface(const bNodeTree &node_tree, const bNodeSocket &socket)
static wmOperatorStatus node_group_separate_exec(bContext *C, wmOperator *op)
bNode * node_under_mouse_get(const SpaceNode &snode, const float2 mouse)
static wmOperatorStatus node_group_edit_exec(bContext *C, wmOperator *op)
VectorSet< bNode * > get_selected_nodes(bNodeTree &node_tree)
static bool node_group_operator_editable(bContext *C)
Definition node_group.cc:95
float2 space_node_group_offset(const SpaceNode &snode)
static wmOperatorStatus node_group_make_exec(bContext *C, wmOperator *op)
static void get_min_max_of_nodes(const Span< bNode * > nodes, const bool use_size, float2 &min, float2 &max)
void NODE_OT_group_edit(wmOperatorType *ot)
static void update_nested_node_refs_after_ungroup(bNodeTree &ntree, const bNodeTree &ngroup, const bNode &gnode, const Map< int32_t, int32_t > &node_identifier_map)
void NODE_OT_group_separate(wmOperatorType *ot)
static bNode * node_group_make_from_nodes(const bContext &C, bNodeTree &ntree, const VectorSet< bNode * > &nodes_to_group, const StringRef ntype, const StringRef ntreetype)
void NODE_OT_group_enter_exit(wmOperatorType *ot)
static void remap_pairing(bNodeTree &dst_tree, Span< bNode * > nodes, const Map< int32_t, int32_t > &identifier_map)
void NODE_OT_default_group_width_set(wmOperatorType *ot)
static wmOperatorStatus node_group_enter_exit_invoke(bContext *C, wmOperator *, const wmEvent *event)
void NODE_OT_group_ungroup(wmOperatorType *ot)
static bNodeTree * node_group_make_wrapper(const bContext &C, const bNodeTree &src_tree, const bNode &src_node, WrapperNodeGroupMapping &r_mapping)
StringRef node_group_idname(const bContext *C)
static StringRef group_ntree_idname(bContext *C)
static void animation_basepath_change_free(AnimationBasePathChange *basepath_change)
T midpoint(const T &a, const T &b)
void min_max(const T &value, T &min, T &max)
void update_node_declaration_and_sockets(bNodeTree &ntree, bNode &node)
VecBase< float, 2 > float2
void uninitialized_copy_n(const T *src, int64_t n, T *dst)
bNodeSocket * node_group_output_find_socket(bNode *node, const StringRef identifier)
bNodeSocket * node_group_input_find_socket(bNode *node, const StringRef identifier)
bNodeSocket * node_group_find_input_socket(bNode *groupnode, const blender::StringRef identifier)
bNodeSocket * node_group_find_output_socket(bNode *groupnode, const blender::StringRef identifier)
blender::bke::bNodeTreeType * ntreeType_Composite
blender::bke::bNodeTreeType * ntreeType_Geometry
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
std::optional< std::string > RNA_path_from_ID_to_struct(const PointerRNA *ptr)
Definition rna_path.cc:1014
#define min(a, b)
Definition sort.cc:36
bAction * action
const char * dst_basepath
const char * src_basepath
Definition DNA_ID.h:414
void * last
void * first
char tree_idname[64]
ListBase treepath
struct bNodeTree * edittree
struct bNodeTree * nodetree
bNestedNodePath path
char idname[64]
struct bNodeTree * nodetree
struct bNodeTreePath * prev
bNodeTreeRuntimeHandle * runtime
char idname[64]
int nested_node_refs_num
bNestedNodeRef * nested_node_refs
int default_group_node_width
bNodeTreeInterface tree_interface
ListBase nodes
ListBase links
struct AnimData * adt
float location[2]
float width
struct ID * id
struct bNode * parent
char name[64]
char idname[64]
int32_t identifier
bNodeSocket * get_new_input(const bNodeSocket *old_socket, bNode &new_node) const
Map< const bNodeSocket *, int > new_index_by_src_socket
bNodeSocket * get_new_output(const bNodeSocket *old_socket, bNode &new_node) const
void operator_context_set(blender::wm::OpCallContext opcontext)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
wmEventType type
Definition WM_types.hh:757
int mval[2]
Definition WM_types.hh:763
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
#define ISMOUSE_BUTTON(event_type)
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
uint8_t flag
Definition wm_window.cc:145