Nodes: improve socket picking tolerances

The goal here is to make it easier to make node links. Previously, it was quite
easy to accidentally start box selection or to trigger the link-drag-search when
that was not intended.

Now, the tolerances are a bit easier to work with. Also, instead of trying to use
the first socket that is close enough, it will find the closest socket instead. It feels
much better in my testing already, but obviously the values can be tuned more
with some testing.

Also we have to make sure to not accidentally make other things like resizing
nodes harder.

Pull Request: https://projects.blender.org/blender/blender/pulls/115010
This commit is contained in:
Jacques Lucke
2023-11-25 15:23:31 +01:00
parent cea7b0bde8
commit 74dd1e044b
6 changed files with 60 additions and 58 deletions

View File

@@ -3399,14 +3399,14 @@ static const bNode *find_node_under_cursor(SpaceNode &snode, const float2 &curso
return nullptr;
}
void node_set_cursor(wmWindow &win, SpaceNode &snode, const float2 &cursor)
void node_set_cursor(wmWindow &win, ARegion &region, SpaceNode &snode, const float2 &cursor)
{
const bNodeTree *ntree = snode.edittree;
if (ntree == nullptr) {
WM_cursor_set(&win, WM_CURSOR_DEFAULT);
return;
}
if (node_find_indicated_socket(snode, cursor, SOCK_IN | SOCK_OUT)) {
if (node_find_indicated_socket(snode, region, cursor, SOCK_IN | SOCK_OUT)) {
WM_cursor_set(&win, WM_CURSOR_DEFAULT);
return;
}

View File

@@ -1162,70 +1162,68 @@ static bool cursor_isect_multi_input_socket(const float2 &cursor, const bNodeSoc
}
bNodeSocket *node_find_indicated_socket(SpaceNode &snode,
ARegion &region,
const float2 &cursor,
const eNodeSocketInOut in_out)
{
rctf rect;
const float size_sock_padded = NODE_SOCKSIZE + 4;
const float view2d_scale = UI_view2d_scale_get_x(&region.v2d);
const float max_distance = NODE_SOCKSIZE + std::clamp(20.0f / view2d_scale, 5.0f, 30.0f);
bNodeTree &node_tree = *snode.edittree;
node_tree.ensure_topology_cache();
bNodeTree &tree = *snode.edittree;
tree.ensure_topology_cache();
const Array<bNode *> sorted_nodes = tree_draw_order_calc_nodes_reversed(*snode.edittree);
const Array<bNode *> sorted_nodes = tree_draw_order_calc_nodes_reversed(tree);
if (sorted_nodes.is_empty()) {
return nullptr;
}
for (const int i : sorted_nodes.index_range()) {
bNode &node = *sorted_nodes[i];
float best_distance = FLT_MAX;
bNodeSocket *best_socket = nullptr;
BLI_rctf_init_pt_radius(&rect, cursor, size_sock_padded);
if (!(node.flag & NODE_HIDDEN)) {
/* Extra padding inside and out - allow dragging on the text areas too. */
if (in_out == SOCK_IN) {
rect.xmax += NODE_SOCKSIZE;
rect.xmin -= NODE_SOCKSIZE * 4;
}
else if (in_out == SOCK_OUT) {
rect.xmax += NODE_SOCKSIZE * 4;
rect.xmin -= NODE_SOCKSIZE;
}
auto update_best_socket = [&](bNodeSocket *socket, const float distance) {
if (socket_is_occluded(socket->runtime->location, socket->owner_node(), sorted_nodes)) {
return;
}
if (distance < best_distance) {
best_distance = distance;
best_socket = socket;
}
};
for (bNode *node : sorted_nodes) {
if (in_out & SOCK_IN) {
for (bNodeSocket *sock : node.input_sockets()) {
if (node.is_socket_icon_drawn(*sock)) {
const float2 location = sock->runtime->location;
if (sock->flag & SOCK_MULTI_INPUT && !(node.flag & NODE_HIDDEN)) {
if (cursor_isect_multi_input_socket(cursor, *sock)) {
if (!socket_is_occluded(location, node, sorted_nodes)) {
return sock;
}
}
}
else if (BLI_rctf_isect_pt(&rect, location.x, location.y)) {
if (!socket_is_occluded(location, node, sorted_nodes)) {
return sock;
}
for (bNodeSocket *sock : node->input_sockets()) {
if (!node->is_socket_icon_drawn(*sock)) {
continue;
}
const float2 location = sock->runtime->location;
const float distance = math::distance(location, cursor);
if (sock->flag & SOCK_MULTI_INPUT && !(node->flag & NODE_HIDDEN)) {
if (cursor_isect_multi_input_socket(cursor, *sock)) {
update_best_socket(sock, distance);
continue;
}
}
if (distance < max_distance) {
update_best_socket(sock, distance);
}
}
}
if (in_out & SOCK_OUT) {
for (bNodeSocket *sock : node.output_sockets()) {
if (node.is_socket_icon_drawn(*sock)) {
const float2 location = sock->runtime->location;
if (BLI_rctf_isect_pt(&rect, location.x, location.y)) {
if (!socket_is_occluded(location, node, sorted_nodes)) {
return sock;
}
}
for (bNodeSocket *sock : node->output_sockets()) {
if (!node->is_socket_icon_drawn(*sock)) {
continue;
}
const float2 location = sock->runtime->location;
const float distance = math::distance(location, cursor);
if (distance < max_distance) {
update_best_socket(sock, distance);
}
}
}
}
return nullptr;
return best_socket;
}
/** \} */

View File

@@ -187,7 +187,7 @@ Array<bNode *> tree_draw_order_calc_nodes(bNodeTree &ntree);
/** Return the nodes in reverse draw order, with the top nodes at the start. */
Array<bNode *> tree_draw_order_calc_nodes_reversed(bNodeTree &ntree);
void node_set_cursor(wmWindow &win, SpaceNode &snode, const float2 &cursor);
void node_set_cursor(wmWindow &win, ARegion &region, SpaceNode &snode, const float2 &cursor);
/* DPI scaled coords */
float2 node_to_view(const bNode &node, const float2 &co);
void node_to_updated_rect(const bNode &node, rctf &r_rect);
@@ -350,6 +350,7 @@ void node_set_hidden_sockets(bNode *node, int set);
bool node_is_previewable(const SpaceNode &snode, const bNodeTree &ntree, const bNode &node);
int node_render_changed_exec(bContext *, wmOperator *);
bNodeSocket *node_find_indicated_socket(SpaceNode &snode,
ARegion &region,
const float2 &cursor,
eNodeSocketInOut in_out);
float node_link_dim_factor(const View2D &v2d, const bNodeLink &link);

View File

@@ -122,11 +122,12 @@ static void pick_input_link_by_link_intersect(const bContext &C,
const float2 &cursor)
{
SpaceNode *snode = CTX_wm_space_node(&C);
ARegion *region = CTX_wm_region(&C);
bNodeTree &node_tree = *snode->edittree;
float2 drag_start;
RNA_float_get_array(op.ptr, "drag_start", drag_start);
bNodeSocket *socket = node_find_indicated_socket(*snode, drag_start, SOCK_IN);
bNodeSocket *socket = node_find_indicated_socket(*snode, *region, drag_start, SOCK_IN);
bNode &node = socket->owner_node();
/* Distance to test overlapping of cursor on link. */
@@ -163,7 +164,7 @@ static void pick_input_link_by_link_intersect(const bContext &C,
link_to_pick->flag |= NODE_LINK_TEMP_HIGHLIGHT;
ED_area_tag_redraw(CTX_wm_area(&C));
if (!node_find_indicated_socket(*snode, cursor, SOCK_IN)) {
if (!node_find_indicated_socket(*snode, *region, cursor, SOCK_IN)) {
pick_link(nldrag, *snode, &node, *link_to_pick);
}
}
@@ -1110,10 +1111,11 @@ static void node_link_cancel(bContext *C, wmOperator *op)
static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cursor)
{
SpaceNode &snode = *CTX_wm_space_node(&C);
ARegion &region = *CTX_wm_region(&C);
bNodeLinkDrag &nldrag = *static_cast<bNodeLinkDrag *>(op.customdata);
if (nldrag.in_out == SOCK_OUT) {
if (bNodeSocket *tsock = node_find_indicated_socket(snode, cursor, SOCK_IN)) {
if (bNodeSocket *tsock = node_find_indicated_socket(snode, region, cursor, SOCK_IN)) {
nldrag.hovered_socket = tsock;
bNode &tnode = tsock->owner_node();
for (bNodeLink &link : nldrag.links) {
@@ -1158,7 +1160,7 @@ static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cur
}
}
else {
if (bNodeSocket *tsock = node_find_indicated_socket(snode, cursor, SOCK_OUT)) {
if (bNodeSocket *tsock = node_find_indicated_socket(snode, region, cursor, SOCK_OUT)) {
nldrag.hovered_socket = tsock;
bNode &node = tsock->owner_node();
for (bNodeLink &link : nldrag.links) {
@@ -1283,11 +1285,12 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
static std::unique_ptr<bNodeLinkDrag> node_link_init(SpaceNode &snode,
static std::unique_ptr<bNodeLinkDrag> node_link_init(ARegion &region,
SpaceNode &snode,
const float2 cursor,
const bool detach)
{
if (bNodeSocket *sock = node_find_indicated_socket(snode, cursor, SOCK_OUT)) {
if (bNodeSocket *sock = node_find_indicated_socket(snode, region, cursor, SOCK_OUT)) {
bNode &node = sock->owner_node();
std::unique_ptr<bNodeLinkDrag> nldrag = std::make_unique<bNodeLinkDrag>();
@@ -1318,7 +1321,7 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(SpaceNode &snode,
return nldrag;
}
if (bNodeSocket *sock = node_find_indicated_socket(snode, cursor, SOCK_IN)) {
if (bNodeSocket *sock = node_find_indicated_socket(snode, region, cursor, SOCK_IN)) {
bNode &node = sock->owner_node();
std::unique_ptr<bNodeLinkDrag> nldrag = std::make_unique<bNodeLinkDrag>();
nldrag->last_node_hovered_while_dragging_a_link = &node;
@@ -1377,7 +1380,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
std::unique_ptr<bNodeLinkDrag> nldrag = node_link_init(snode, cursor, detach);
std::unique_ptr<bNodeLinkDrag> nldrag = node_link_init(region, snode, cursor, detach);
if (!nldrag) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}

View File

@@ -174,12 +174,12 @@ static bool node_under_mouse_tweak(const SpaceNode &snode, const float2 &mouse)
return false;
}
static bool is_position_over_node_or_socket(SpaceNode &snode, const float2 &mouse)
static bool is_position_over_node_or_socket(SpaceNode &snode, ARegion &region, const float2 &mouse)
{
if (node_under_mouse_tweak(snode, mouse)) {
return true;
}
if (node_find_indicated_socket(snode, mouse, SOCK_IN | SOCK_OUT)) {
if (node_find_indicated_socket(snode, region, mouse, SOCK_IN | SOCK_OUT)) {
return true;
}
return false;
@@ -195,7 +195,7 @@ static bool is_event_over_node_or_socket(const bContext &C, const wmEvent &event
float2 mouse;
UI_view2d_region_to_view(&region.v2d, mval.x, mval.y, &mouse.x, &mouse.y);
return is_position_over_node_or_socket(snode, mouse);
return is_position_over_node_or_socket(snode, region, mouse);
}
void node_socket_select(bNode *node, bNodeSocket &sock)
@@ -566,7 +566,7 @@ static bool node_mouse_select(bContext *C,
if (socket_select) {
/* NOTE: unlike nodes #SelectPick_Params isn't fully supported. */
const bool extend = (params->sel_op == SEL_OP_XOR);
sock = node_find_indicated_socket(snode, cursor, SOCK_IN);
sock = node_find_indicated_socket(snode, region, cursor, SOCK_IN);
if (sock) {
node = &sock->owner_node();
found = true;
@@ -578,7 +578,7 @@ static bool node_mouse_select(bContext *C,
changed = true;
}
if (!changed) {
sock = node_find_indicated_socket(snode, cursor, SOCK_OUT);
sock = node_find_indicated_socket(snode, region, cursor, SOCK_OUT);
if (sock) {
node = &sock->owner_node();
found = true;

View File

@@ -766,7 +766,7 @@ static void node_cursor(wmWindow *win, ScrArea *area, ARegion *region)
&snode->runtime->cursor[1]);
/* here snode->runtime->cursor is used to detect the node edge for sizing */
node_set_cursor(*win, *snode, snode->runtime->cursor);
node_set_cursor(*win, *region, *snode, snode->runtime->cursor);
/* XXX snode->runtime->cursor is in placing new nodes space */
snode->runtime->cursor[0] /= UI_SCALE_FAC;