From ebb61ef30f716918592c310568b2d154dfde72a4 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 23 May 2024 19:52:37 +0200 Subject: [PATCH] Nodes: avoid removing link when inserting incompatible node Previously, when dropping a node on a link with incompatible sockets, the link would be removed. While sometimes useful in super simple setup, it is generally useless for most work. Even worse, users might not notice that they accidentally removed a link (this has been reported in the recent geometry nodes workshop). This patch changes this behavior in two ways: * A node can't be inserted onto an incompatible link anymore. * A link will be dimmed while dragging a node over it, if it is incompatible or if alt is pressed. Pull Request: https://projects.blender.org/blender/blender/pulls/121975 --- source/blender/editors/include/ED_node.hh | 2 +- source/blender/editors/space_node/drawnode.cc | 2 +- .../blender/editors/space_node/node_edit.cc | 3 ++ .../editors/space_node/node_relationships.cc | 39 ++++++++++++++++--- .../transform/transform_convert_node.cc | 15 ++----- source/blender/makesdna/DNA_node_types.h | 5 +++ 6 files changed, 47 insertions(+), 19 deletions(-) diff --git a/source/blender/editors/include/ED_node.hh b/source/blender/editors/include/ED_node.hh index 411d4e25884..686ca24277f 100644 --- a/source/blender/editors/include/ED_node.hh +++ b/source/blender/editors/include/ED_node.hh @@ -27,7 +27,7 @@ namespace blender::ed::space_node { VectorSet get_selected_nodes(bNodeTree &node_tree); -void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion ®ion); +void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion ®ion, bool attach_enabled); /** * Assumes link with #NODE_LINK_INSERT_TARGET set. diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index fba73bcb604..ddfa6251f7a 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -2323,7 +2323,7 @@ void node_draw_link(const bContext &C, if (link.flag & NODE_LINK_VALID) { /* special indicated link, on drop-node */ - if (link.flag & NODE_LINK_INSERT_TARGET) { + if (link.flag & NODE_LINK_INSERT_TARGET && !(link.flag & NODE_LINK_INSERT_TARGET_INVALID)) { th_col1 = th_col2 = TH_ACTIVE; } else if (link.flag & NODE_LINK_MUTED) { diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 5c83ef18ffe..db64a5aede2 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -1283,6 +1283,9 @@ float node_link_dim_factor(const View2D &v2d, const bNodeLink &link) if (link.fromsock == nullptr || link.tosock == nullptr) { return 1.0f; } + if (link.flag & NODE_LINK_INSERT_TARGET_INVALID) { + return 0.2f; + } const float2 from = link.fromsock->runtime->location; const float2 to = link.tosock->runtime->location; diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index cf1c1679177..e45d2d9636c 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -2108,7 +2108,32 @@ static bNode *get_selected_node_for_insertion(bNodeTree &node_tree) return selected_node; } -void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion ®ion) +static bool node_can_be_inserted_on_link(bNodeTree &tree, bNode &node, const bNodeLink &link) +{ + const bNodeSocket *main_input = get_main_socket(tree, node, SOCK_IN); + const bNodeSocket *main_output = get_main_socket(tree, node, SOCK_IN); + if (ELEM(nullptr, main_input, main_output)) { + return false; + } + if (!tree.typeinfo->validate_link) { + return true; + } + if (!tree.typeinfo->validate_link(eNodeSocketDatatype(link.fromsock->type), + eNodeSocketDatatype(main_input->type))) + { + return false; + } + if (!tree.typeinfo->validate_link(eNodeSocketDatatype(main_output->type), + eNodeSocketDatatype(link.tosock->type))) + { + return false; + } + return true; +} + +void node_insert_on_link_flags_set(SpaceNode &snode, + const ARegion ®ion, + const bool attach_enabled) { bNodeTree &node_tree = *snode.edittree; node_tree.ensure_topology_cache(); @@ -2156,13 +2181,16 @@ void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion ®ion) if (selink) { selink->flag |= NODE_LINK_INSERT_TARGET; + if (!attach_enabled || !node_can_be_inserted_on_link(node_tree, *node_to_insert, *selink)) { + selink->flag |= NODE_LINK_INSERT_TARGET_INVALID; + } } } void node_insert_on_link_flags_clear(bNodeTree &node_tree) { LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) { - link->flag &= ~NODE_LINK_INSERT_TARGET; + link->flag &= ~(NODE_LINK_INSERT_TARGET | NODE_LINK_INSERT_TARGET_INVALID); } } @@ -2180,16 +2208,17 @@ void node_insert_on_link_flags(Main &bmain, SpaceNode &snode) bNodeLink *old_link = nullptr; LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { if (link->flag & NODE_LINK_INSERT_TARGET) { - old_link = link; + if (!(link->flag & NODE_LINK_INSERT_TARGET_INVALID)) { + old_link = link; + } break; } } + node_insert_on_link_flags_clear(node_tree); if (old_link == nullptr) { return; } - old_link->flag &= ~NODE_LINK_INSERT_TARGET; - bNodeSocket *best_input = get_main_socket(ntree, *node_to_insert, SOCK_IN); bNodeSocket *best_output = get_main_socket(ntree, *node_to_insert, SOCK_OUT); diff --git a/source/blender/editors/transform/transform_convert_node.cc b/source/blender/editors/transform/transform_convert_node.cc index c0cad31607e..6a28990ebe9 100644 --- a/source/blender/editors/transform/transform_convert_node.cc +++ b/source/blender/editors/transform/transform_convert_node.cc @@ -110,12 +110,7 @@ static void createTransNodeData(bContext * /*C*/, TransInfo *t) NODE_EDGE_PAN_ZOOM_INFLUENCE); customdata->viewrect_prev = customdata->edgepan_data.initial_rect; - if (t->modifiers & MOD_NODE_ATTACH) { - space_node::node_insert_on_link_flags_set(*snode, *t->region); - } - else { - space_node::node_insert_on_link_flags_clear(*snode->edittree); - } + space_node::node_insert_on_link_flags_set(*snode, *t->region, t->modifiers & MOD_NODE_ATTACH); t->custom.type.data = customdata; t->custom.type.use_free = true; @@ -243,12 +238,8 @@ static void flushTransNodes(TransInfo *t) /* Handle intersection with noodles. */ if (tc->data_len == 1) { - if (t->modifiers & MOD_NODE_ATTACH) { - space_node::node_insert_on_link_flags_set(*snode, *t->region); - } - else { - space_node::node_insert_on_link_flags_clear(*snode->edittree); - } + space_node::node_insert_on_link_flags_set( + *snode, *t->region, t->modifiers & MOD_NODE_ATTACH); } } } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 8fd34f36310..c0090f94370 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -627,6 +627,11 @@ enum { NODE_LINK_TEMP_HIGHLIGHT = 1 << 3, /** Link is muted. */ NODE_LINK_MUTED = 1 << 4, + /** + * The dragged node would be inserted here, but this link is ignored because it's not compatible + * with the node. + */ + NODE_LINK_INSERT_TARGET_INVALID = 1 << 5, }; typedef struct bNestedNodePath {