UI: Multi-input node socket spacing and interaction

This commit makes links connected to multi-input sockets spread verticaly
along the socket. Sockets grow if more links are connected and the node
layout updates accordingly. Links are sorted by their incoming angle
to avoid crossing links. Also, link picking is updated to work with
spread links and bezier links.

Currently the multi-input sockets are used in the join geometry node.

The mutli-input sockets look like a vertical rounded rectangle.
Currently they do not support the other custom socket shapes.

Reviewed By Hans Goudey, with cleanup and additional edits

Differential Revision: https://developer.blender.org/D10181
This commit is contained in:
Fabian Schempp 2021-02-11 01:16:17 -06:00 committed by Hans Goudey
parent 9a9e19fdcc
commit 8f707a72e8
Notes: blender-bot 2023-02-14 01:11:05 +01:00
Referenced by commit 69e191604b, Fix T85541: crash when replacing an existing node link
Referenced by issue #85541, Geometry Nodes - Connecting Geometry output to Geometry input causes Blender to crash
7 changed files with 391 additions and 34 deletions

View File

@ -573,6 +573,7 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
sock->typeinfo = nullptr;
BLO_read_data_address(reader, &sock->storage);
BLO_read_data_address(reader, &sock->default_value);
sock->total_inputs = 0; /* Clear runtime data set before drawing. */
sock->cache = nullptr;
}

View File

@ -3560,10 +3560,10 @@ void draw_nodespace_back_pix(const bContext *C,
}
/* return quadratic beziers points for a given nodelink and clip if v2d is not NULL. */
static bool node_link_bezier_handles(const View2D *v2d,
const SpaceNode *snode,
const bNodeLink *link,
float vec[4][2])
bool node_link_bezier_handles(const View2D *v2d,
const SpaceNode *snode,
const bNodeLink *link,
float vec[4][2])
{
float cursor[2] = {0.0f, 0.0f};
@ -3591,6 +3591,9 @@ static bool node_link_bezier_handles(const View2D *v2d,
if (link->tosock) {
vec[3][0] = link->tosock->locx;
vec[3][1] = link->tosock->locy;
if (!(link->tonode->flag & NODE_HIDDEN) && link->tosock->flag & SOCK_MULTI_INPUT) {
node_link_calculate_multi_input_position(link, vec[3]);
}
toreroute = (link->tonode && link->tonode->type == NODE_REROUTE);
}
else {
@ -3902,7 +3905,7 @@ void node_draw_link_bezier(const View2D *v2d,
int th_col3)
{
float vec[4][2];
const bool highlighted = link->flag & NODE_LINK_TEMP_HIGHLIGHT;
if (node_link_bezier_handles(v2d, snode, link, vec)) {
int drawarrow = ((link->tonode && (link->tonode->type == NODE_REROUTE)) &&
(link->fromnode && (link->fromnode->type == NODE_REROUTE)));
@ -3911,7 +3914,7 @@ void node_draw_link_bezier(const View2D *v2d,
nodelink_batch_init();
}
if (g_batch_link.enabled) {
if (g_batch_link.enabled && !highlighted) {
/* Add link to batch. */
nodelink_batch_add_link(
snode, vec[0], vec[1], vec[2], vec[3], th_col1, th_col2, th_col3, drawarrow);
@ -3925,6 +3928,12 @@ void node_draw_link_bezier(const View2D *v2d,
UI_GetThemeColor4fv(th_col1, colors[1]);
UI_GetThemeColor4fv(th_col2, colors[2]);
if (highlighted) {
float link_preselection_highlight_color[4];
UI_GetThemeColor4fv(TH_SELECT, link_preselection_highlight_color);
copy_v4_v4(colors[2], link_preselection_highlight_color);
}
GPUBatch *batch = g_batch_link.batch_single;
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_NODELINK);
GPU_batch_uniform_2fv_array(batch, "bezierPts", 4, vec);

View File

@ -22,6 +22,8 @@
* \brief higher level node drawing for the node editor.
*/
#include "MEM_guardedalloc.h"
#include "DNA_light_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_material_types.h"
@ -500,6 +502,16 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
PointerRNA sockptr;
RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr);
/* Add the half the height of a multi-input socket to cursor Y
* to account for the increased height of the taller sockets. */
float multi_input_socket_offset = 0.0f;
if (nsock->flag & SOCK_MULTI_INPUT) {
if (nsock->total_inputs > 2) {
multi_input_socket_offset = (nsock->total_inputs - 2) * NODE_MULTI_INPUT_LINK_GAP;
}
}
dy -= multi_input_socket_offset * 0.5f;
uiLayout *layout = UI_block_layout(node->block,
UI_LAYOUT_VERTICAL,
UI_LAYOUT_PANEL,
@ -533,7 +545,7 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
/* place the socket circle in the middle of the layout */
nsock->locy = 0.5f * (dy + buty);
dy = buty;
dy = buty - multi_input_socket_offset * 0.5;
if (nsock->next) {
dy -= NODE_SOCKDY;
}
@ -751,6 +763,27 @@ static void node_socket_draw(const bNodeSocket *sock,
immVertex2f(pos_id, locx, locy);
}
static void node_socket_draw_multi_input(const float color[4],
const float color_outline[4],
const float width,
const float height,
const int locx,
const int locy)
{
const float outline_width = 1.0f;
/* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width. */
const rctf rect = {
.xmin = locx - width + outline_width * 0.5f,
.xmax = locx + width - outline_width * 0.5f,
.ymin = locy - height + outline_width * 0.5f,
.ymax = locy + height - outline_width * 0.5f,
};
UI_draw_roundbox_corner_set(UI_CNR_ALL);
UI_draw_roundbox_4fv_ex(
&rect, color, NULL, 1.0f, color_outline, outline_width, width - outline_width * 0.5f);
}
static void node_socket_outline_color_get(bool selected, float r_outline_color[4])
{
if (selected) {
@ -1006,6 +1039,10 @@ void node_draw_sockets(const View2D *v2d,
selected_input_len++;
continue;
}
/* Don't draw multi-input sockets here since they are drawn in a different batch. */
if (sock->flag & SOCK_MULTI_INPUT) {
continue;
}
node_socket_draw_nested(C,
ntree,
@ -1115,6 +1152,28 @@ void node_draw_sockets(const View2D *v2d,
GPU_program_point_size(false);
GPU_blend(GPU_BLEND_NONE);
/* Draw multi-nput sockets after the others because they are drawn with "UI_roundbox"
* rather than with GL_POINT. */
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
if (nodeSocketIsHidden(socket)) {
continue;
}
if (!(socket->flag & SOCK_MULTI_INPUT)) {
continue;
}
const bool is_node_hidden = (node->flag & NODE_HIDDEN);
const float width = NODE_SOCKSIZE;
float height = is_node_hidden ? width : node_socket_calculate_height(socket) - width;
float color[4];
float outline_color[4];
node_socket_color_get((bContext *)C, ntree, &node_ptr, socket, color);
node_socket_outline_color_get(selected, outline_color);
node_socket_draw_multi_input(color, outline_color, width, height, socket->locx, socket->locy);
}
}
static void node_draw_basis(const bContext *C,
@ -1590,17 +1649,67 @@ static void node_update(const bContext *C, bNodeTree *ntree, bNode *node)
}
}
static void count_mutli_input_socket_links(bNodeTree *ntree, SpaceNode *snode)
{
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
LISTBASE_FOREACH (struct bNodeSocket *, socket, &node->inputs) {
if (socket->flag & SOCK_MULTI_INPUT) {
socket->total_inputs = 0;
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (link->tosock == socket) {
socket->total_inputs++;
}
}
/* Count temporary links going into this socket. */
LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) {
LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
bNodeLink *link = (bNodeLink *)linkdata->data;
if (link->tosock == socket) {
socket->total_inputs++;
}
}
}
}
}
}
}
void node_update_nodetree(const bContext *C, bNodeTree *ntree)
{
/* make sure socket "used" tags are correct, for displaying value buttons */
SpaceNode *snode = CTX_wm_space_node(C);
ntreeTagUsedSockets(ntree);
count_mutli_input_socket_links(ntree, snode);
/* update nodes front to back, so children sizes get updated before parents */
LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree->nodes) {
node_update(C, ntree, node);
}
}
static int compare_link_by_angle_to_node(const void *a, const void *b)
{
const bNodeLink *link_a = *(const bNodeLink **)a;
const bNodeLink *link_b = *(const bNodeLink **)b;
BLI_assert(link_a->tosock == link_b->tosock);
const float socket_location[2] = {link_a->tosock->locx, link_a->tosock->locy};
const float up_direction[2] = {0.0f, 1.0f};
float delta_a[2] = {link_a->fromsock->locx - socket_location[0],
link_a->fromsock->locy - socket_location[1]};
normalize_v2(delta_a);
const float angle_a = angle_normalized_v2v2(up_direction, delta_a);
float delta_b[2] = {link_b->fromsock->locx - socket_location[0],
link_b->fromsock->locy - socket_location[1]};
normalize_v2(delta_b);
const float angle_b = angle_normalized_v2v2(up_direction, delta_b);
return angle_a < angle_b ? 1 : -1;
}
static void node_draw(const bContext *C,
ARegion *region,
SpaceNode *snode,
@ -1615,6 +1724,46 @@ static void node_draw(const bContext *C,
#define USE_DRAW_TOT_UPDATE
/**
* Automatically sort the input links to multi-input sockets to avoid crossing noodles.
*/
static void sort_multi_input_socket_links(bNodeTree *ntree, SpaceNode *snode)
{
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
if (socket->flag & SOCK_MULTI_INPUT) {
/* The total is calculated in #node_update_nodetree, which runs before this draw step. */
const int total_inputs = socket->total_inputs;
bNodeLink **input_links = MEM_malloc_arrayN(total_inputs, sizeof(bNodeLink *), __func__);
int index = 0;
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (link->tosock == socket) {
input_links[index] = (bNodeLink *)link;
index++;
}
}
LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) {
LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
bNodeLink *link = (bNodeLink *)linkdata->data;
if (link->tosock == socket) {
input_links[index] = (bNodeLink *)link;
index++;
}
}
}
qsort(input_links, total_inputs, sizeof(bNodeLink *), compare_link_by_angle_to_node);
for (int i = 0; i < total_inputs; i++) {
input_links[i]->multi_input_socket_index = i;
}
MEM_freeN(input_links);
}
}
}
}
void node_draw_nodetree(const bContext *C,
ARegion *region,
SpaceNode *snode,
@ -1650,6 +1799,9 @@ void node_draw_nodetree(const bContext *C,
/* node lines */
GPU_blend(GPU_BLEND_ALPHA);
nodelink_batch_start(snode);
sort_multi_input_socket_links(ntree, snode);
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (!nodeLinkIsHidden(link)) {
node_draw_link(&region->v2d, snode, link);

View File

@ -98,6 +98,25 @@ typedef struct CompoJob {
float *progress;
} CompoJob;
float node_socket_calculate_height(const bNodeSocket *socket)
{
float sock_height = NODE_SOCKSIZE * 2.0f;
if (socket->flag & SOCK_MULTI_INPUT) {
sock_height += max_ii(NODE_MULTI_INPUT_LINK_GAP * 0.5f * socket->total_inputs, NODE_SOCKSIZE);
}
return sock_height;
}
void node_link_calculate_multi_input_position(const bNodeLink *link, float r[2])
{
float offset = (link->tosock->total_inputs * NODE_MULTI_INPUT_LINK_GAP -
NODE_MULTI_INPUT_LINK_GAP) *
0.5;
r[0] = link->tosock->locx - NODE_SOCKSIZE * 0.5f;
r[1] = link->tosock->locy - offset +
(link->multi_input_socket_index * NODE_MULTI_INPUT_LINK_GAP);
}
static void compo_tag_output_nodes(bNodeTree *nodetree, int recalc_flags)
{
LISTBASE_FOREACH (bNode *, node, &nodetree->nodes) {
@ -1104,6 +1123,21 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
}
/* checks snode->mouse position, and returns found node/socket */
static bool cursor_isect_multi_input_socket(const float cursor[2], const bNodeSocket *socket)
{
const float node_socket_height = node_socket_calculate_height(socket);
const rctf multi_socket_rect = {
.xmin = socket->locx - NODE_SOCKSIZE * 4,
.xmax = socket->locx + NODE_SOCKSIZE,
.ymin = socket->locy - node_socket_height * 0.5 - NODE_SOCKSIZE * 2.0f,
.ymax = socket->locy + node_socket_height * 0.5 + NODE_SOCKSIZE * 2.0f,
};
if (BLI_rctf_isect_pt(&multi_socket_rect, cursor[0], cursor[1])) {
return true;
}
return false;
}
/* type is SOCK_IN and/or SOCK_OUT */
int node_find_indicated_socket(
SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, float cursor[2], int in_out)
@ -1132,7 +1166,16 @@ int node_find_indicated_socket(
if (in_out & SOCK_IN) {
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (!nodeSocketIsHidden(sock)) {
if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) {
if (sock->flag & SOCK_MULTI_INPUT && !(node->flag & NODE_HIDDEN)) {
if (cursor_isect_multi_input_socket(cursor, sock)) {
if (node == visible_node(snode, &rect)) {
*nodep = node;
*sockp = sock;
return 1;
}
}
}
else if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) {
if (node == visible_node(snode, &rect)) {
*nodep = node;
*sockp = sock;

View File

@ -78,6 +78,9 @@ typedef struct SpaceNode_Runtime {
void space_node_group_offset(struct SpaceNode *snode, float *x, float *y);
/* node_draw.c */
float node_socket_calculate_height(const bNodeSocket *socket);
void node_link_calculate_multi_input_position(const bNodeLink *link, float r[2]);
int node_get_colorid(struct bNode *node);
int node_get_resize_cursor(int directions);
void node_draw_shadow(const struct SpaceNode *snode,
@ -178,6 +181,10 @@ bool node_link_bezier_points(const struct View2D *v2d,
const struct bNodeLink *link,
float coord_array[][2],
const int resol);
bool node_link_bezier_handles(const struct View2D *v2d,
const struct SpaceNode *snode,
const struct bNodeLink *link,
float vec[4][2]);
void draw_nodespace_back_pix(const struct bContext *C,
struct ARegion *region,
struct SpaceNode *snode,
@ -290,6 +297,7 @@ extern const char *node_context_dir[];
#define NODE_HEIGHT(node) (node->height * UI_DPI_FAC)
#define NODE_MARGIN_X (1.10f * U.widget_unit)
#define NODE_SOCKSIZE (0.25f * U.widget_unit)
#define NODE_MULTI_INPUT_LINK_GAP (0.25f * U.widget_unit)
#define NODE_RESIZE_MARGIN (0.20f * U.widget_unit)
#define NODE_LINK_RESOL 12

View File

@ -32,6 +32,7 @@
#include "BKE_anim_data.h"
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
@ -179,6 +180,137 @@ typedef struct NodeInsertOfsData {
float offset_x; /* offset to apply to node chain */
} NodeInsertOfsData;
static void clear_picking_highlight(ListBase *links)
{
LISTBASE_FOREACH (bNodeLink *, link, links) {
link->flag &= ~NODE_LINK_TEMP_HIGHLIGHT;
}
}
static LinkData *create_drag_link(Main *bmain, SpaceNode *snode, bNode *node, bNodeSocket *sock)
{
LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
linkdata->data = oplink;
if (sock->in_out == SOCK_OUT) {
oplink->fromnode = node;
oplink->fromsock = sock;
}
else {
oplink->tonode = node;
oplink->tosock = sock;
}
oplink->flag |= NODE_LINK_VALID;
oplink->flag &= ~NODE_LINK_TEST;
if (node_connected_to_output(bmain, snode->edittree, node)) {
oplink->flag |= NODE_LINK_TEST;
}
return linkdata;
}
static void pick_link(const bContext *C,
wmOperator *op,
bNodeLinkDrag *nldrag,
SpaceNode *snode,
bNode *node,
bNodeLink *link_to_pick)
{
clear_picking_highlight(&snode->edittree->links);
RNA_boolean_set(op->ptr, "has_link_picked", true);
Main *bmain = CTX_data_main(C);
LinkData *linkdata = create_drag_link(
bmain, snode, link_to_pick->fromnode, link_to_pick->fromsock);
BLI_addtail(&nldrag->links, linkdata);
nodeRemLink(snode->edittree, link_to_pick);
/* Send changed event to original link->tonode. */
if (node) {
snode_update(snode, node);
}
}
static void pick_input_link_by_link_intersect(const bContext *C,
wmOperator *op,
bNodeLinkDrag *nldrag,
const float *cursor)
{
SpaceNode *snode = CTX_wm_space_node(C);
const ARegion *region = CTX_wm_region(C);
const View2D *v2d = &region->v2d;
float drag_start[2];
RNA_float_get_array(op->ptr, "drag_start", drag_start);
bNode *node;
bNodeSocket *socket;
node_find_indicated_socket(snode, &node, &socket, drag_start, SOCK_IN);
const float trigger_drag_distance = 25.0f;
const float cursor_link_touch_distance = 25.0f;
const float socket_height = node_socket_calculate_height(socket);
float cursor_to_socket_relative[2];
float socket_position[2] = {socket->locx, socket->locy};
sub_v2_v2v2(cursor_to_socket_relative, cursor, socket_position);
float distance_from_socket_v2[2] = {
max_ff(0, fabs(cursor_to_socket_relative[0]) - NODE_SOCKSIZE * 0.5),
max_ff(0, fabs(cursor_to_socket_relative[1]) - socket_height)};
const float distance_from_socket = len_v2(distance_from_socket_v2);
const int resolution = NODE_LINK_RESOL;
bNodeLink *link_to_pick = NULL;
clear_picking_highlight(&snode->edittree->links);
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
if (link->tosock == socket) {
/* Test if the cursor is near a link. */
float vec[4][2];
node_link_bezier_handles(v2d, snode, link, vec);
float data[NODE_LINK_RESOL * 2 + 2];
BKE_curve_forward_diff_bezier(
vec[0][0], vec[1][0], vec[2][0], vec[3][0], data, resolution, sizeof(float[2]));
BKE_curve_forward_diff_bezier(
vec[0][1], vec[1][1], vec[2][1], vec[3][1], data + 1, resolution, sizeof(float[2]));
for (int i = 0; i < resolution * 2; i += 2) {
float *l1 = &data[i];
float *l2 = &data[i + 2];
float distance = dist_squared_to_line_segment_v2(cursor, l1, l2);
if (distance < cursor_link_touch_distance) {
link_to_pick = link;
RNA_int_set(op->ptr, "last_picked_link_index", link->multi_input_socket_index);
}
}
}
}
/* If no linked was picked in this call, try using the one picked in the previous call.
* Not essential for the basic behavior, but can make interaction feel a bit better if
* the mouse moves to the right and loses the "selection." */
if (!link_to_pick) {
int last_picked_link_index = RNA_int_get(op->ptr, "last_picked_link_index");
if (last_picked_link_index > -1) {
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
if (link->multi_input_socket_index == last_picked_link_index) {
link_to_pick = link;
}
}
}
}
if (link_to_pick) {
/* Highlight is set here and cleared in the next iteration or if the operation finishes. */
link_to_pick->flag |= NODE_LINK_TEMP_HIGHLIGHT;
ED_area_tag_redraw(CTX_wm_area(C));
if (distance_from_socket > trigger_drag_distance) {
pick_link(C, op, nldrag, snode, node, link_to_pick);
}
}
}
static int sort_nodes_locx(const void *a, const void *b)
{
const bNodeListItem *nli1 = a;
@ -600,6 +732,13 @@ static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link)
tlink = NULL;
to_count--;
}
/* Also remove link if it comes from the same output. */
if (tlink->fromsock == from) {
nodeRemLink(ntree, tlink);
tlink = NULL;
to_count--;
from_count--;
}
}
}
}
@ -748,10 +887,15 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
switch (event->type) {
case MOUSEMOVE:
node_link_find_socket(C, op, cursor);
if (nldrag->from_multi_input_socket && !RNA_boolean_get(op->ptr, "has_link_picked")) {
pick_input_link_by_link_intersect(C, op, nldrag, cursor);
}
else {
node_link_find_socket(C, op, cursor);
node_link_update_header(C, nldrag);
ED_region_tag_redraw(region);
node_link_update_header(C, nldrag);
ED_region_tag_redraw(region);
}
break;
case LEFTMOUSE:
@ -762,6 +906,8 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
ED_workspace_status_text(C, NULL);
ED_region_tag_redraw(region);
SpaceNode *snode = CTX_wm_space_node(C);
clear_picking_highlight(&snode->edittree->links);
return OPERATOR_FINISHED;
}
break;
@ -817,16 +963,7 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor
/* dragged links are fixed on output side */
nldrag->in_out = SOCK_OUT;
/* create a new link */
LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
linkdata->data = oplink;
oplink->fromnode = node;
oplink->fromsock = sock;
oplink->flag |= NODE_LINK_VALID;
oplink->flag &= ~NODE_LINK_TEST;
if (node_connected_to_output(bmain, snode->edittree, node)) {
oplink->flag |= NODE_LINK_TEST;
}
LinkData *linkdata = create_drag_link(bmain, snode, node, sock);
BLI_addtail(&nldrag->links, linkdata);
}
@ -875,16 +1012,7 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor
/* dragged links are fixed on input side */
nldrag->in_out = SOCK_IN;
/* create a new link */
LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
linkdata->data = oplink;
oplink->tonode = node;
oplink->tosock = sock;
oplink->flag |= NODE_LINK_VALID;
oplink->flag &= ~NODE_LINK_TEST;
if (node_connected_to_output(bmain, snode->edittree, node)) {
oplink->flag |= NODE_LINK_TEST;
}
LinkData *linkdata = create_drag_link(bmain, snode, node, sock);
BLI_addtail(&nldrag->links, linkdata);
}
@ -904,6 +1032,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
float cursor[2];
UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]);
RNA_float_set_array(op->ptr, "drag_start", cursor);
RNA_int_set(op->ptr, "last_picked_link_index", -1);
RNA_boolean_set(op->ptr, "has_link_picked", false);
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
@ -931,6 +1060,7 @@ static void node_link_cancel(bContext *C, wmOperator *op)
BLI_freelistN(&nldrag->links);
MEM_freeN(nldrag);
clear_picking_highlight(&snode->edittree->links);
}
void NODE_OT_link(wmOperatorType *ot)
@ -972,6 +1102,16 @@ void NODE_OT_link(wmOperatorType *ot)
-UI_PRECISION_FLOAT_MAX,
UI_PRECISION_FLOAT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN);
RNA_def_int(ot->srna,
"last_picked_link_index",
-1,
-1,
4095,
"Last Picked Link Index",
"The index of the last picked link on a multi-input socket",
-1,
4095);
RNA_def_property_flag(prop, PROP_HIDDEN);
}
/* ********************** Make Link operator ***************** */

View File

@ -118,7 +118,9 @@ typedef struct bNodeSocket {
/* XXX deprecated, kept for forward compatibility */
short stack_type DNA_DEPRECATED;
char display_shape;
char _pad[3];
char _pad[1];
/* Runtime-only cache of the number of input links, for multi-input sockets. */
short total_inputs;
/** Custom dynamic defined label, MAX_NAME. */
char label[64];
@ -391,13 +393,15 @@ typedef struct bNodeLink {
bNodeSocket *fromsock, *tosock;
int flag;
char _pad[4];
/* A runtime storage for automatically sorted links to multi-input sockets. */
int multi_input_socket_index;
} bNodeLink;
/* link->flag */
#define NODE_LINKFLAG_HILITE (1 << 0) /* link has been successfully validated */
#define NODE_LINK_VALID (1 << 1)
#define NODE_LINK_TEST (1 << 2) /* free test flag, undefined */
#define NODE_LINK_TEST (1 << 2) /* free test flag, undefined */
#define NODE_LINK_TEMP_HIGHLIGHT (1 << 3) /* Link is highlighted for picking. */
/* tree->edit_quality/tree->render_quality */
#define NTREE_QUALITY_HIGH 0