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
This commit is contained in:
Jacques Lucke
2024-05-23 19:52:37 +02:00
parent 2e3ad7404a
commit ebb61ef30f
6 changed files with 47 additions and 19 deletions

View File

@@ -27,7 +27,7 @@ namespace blender::ed::space_node {
VectorSet<bNode *> get_selected_nodes(bNodeTree &node_tree);
void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion &region);
void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion &region, bool attach_enabled);
/**
* Assumes link with #NODE_LINK_INSERT_TARGET set.

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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 &region)
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 &region,
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 &region)
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);

View File

@@ -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);
}
}
}

View File

@@ -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 {