Fix: Nodes: double clicking to enter/exit node group not reliable

The issue was that the used operator just took the active node into account.
However, clicking in empty space does not make the active node inactive.
Therefore, sometimes clicking on empty space would sometimes remove enter the
last selected group node. Furthermore, double clicking on other nodes may also
trigger exiting the current group.

This is fixed by introducing a new operator that explicitly checks what node is
hovered.

Pull Request: https://projects.blender.org/blender/blender/pulls/147053
This commit is contained in:
Jacques Lucke
2025-09-30 13:30:49 +02:00
parent da39dc1da8
commit c0de8c40ee
5 changed files with 51 additions and 5 deletions

View File

@@ -2268,8 +2268,7 @@ def km_node_editor(params):
("node.group_make", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
("node.group_ungroup", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("node.group_separate", {"type": 'P', "value": 'PRESS'}, None),
("node.group_edit", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
{"properties": [("exit", False)]}),
("node.group_enter_exit", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
("node.group_edit", {"type": 'TAB', "value": 'PRESS'},
{"properties": [("exit", False)]}),
("node.group_edit", {"type": 'TAB', "value": 'PRESS', "ctrl": True},

View File

@@ -220,6 +220,50 @@ void NODE_OT_group_edit(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Enter group at cursor, or exit when not hovering any node.
* \{ */
static wmOperatorStatus node_group_enter_exit_invoke(bContext *C,
wmOperator * /*op*/,
const wmEvent *event)
{
SpaceNode &snode = *CTX_wm_space_node(C);
ARegion &region = *CTX_wm_region(C);
float2 cursor;
UI_view2d_region_to_view(&region.v2d, event->mval[0], event->mval[1], &cursor.x, &cursor.y);
bNode *node = node_under_mouse_get(snode, cursor);
if (!node || node->is_frame()) {
ED_node_tree_pop(&region, &snode);
return OPERATOR_FINISHED;
}
if (!node->is_group()) {
return OPERATOR_PASS_THROUGH;
}
bNodeTree *group = id_cast<bNodeTree *>(node->id);
if (!group || ID_MISSING(group)) {
return OPERATOR_PASS_THROUGH;
}
ED_node_tree_push(&region, &snode, group, node);
return OPERATOR_FINISHED;
}
void NODE_OT_group_enter_exit(wmOperatorType *ot)
{
ot->name = "Enter/Exit Group";
ot->description = "Enter or exit node group based on cursor location";
ot->idname = "NODE_OT_group_enter_exit";
ot->invoke = node_group_enter_exit_invoke;
ot->poll = node_group_operator_active_poll;
ot->flag = OPTYPE_REGISTER;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Ungroup Operator
* \{ */

View File

@@ -201,6 +201,7 @@ void node_keymap(wmKeyConfig *keyconf);
rctf node_frame_rect_inside(const SpaceNode &snode, const bNode &node);
bool node_or_socket_isect_event(const bContext &C, const wmEvent &event);
bNode *node_under_mouse_get(const SpaceNode &snode, const float2 mouse);
bool node_deselect_all(bNodeTree &node_tree);
void node_socket_select(bNode *node, bNodeSocket &sock);
@@ -322,6 +323,7 @@ void NODE_OT_group_insert(wmOperatorType *ot);
void NODE_OT_group_ungroup(wmOperatorType *ot);
void NODE_OT_group_separate(wmOperatorType *ot);
void NODE_OT_group_edit(wmOperatorType *ot);
void NODE_OT_group_enter_exit(wmOperatorType *ot);
void NODE_OT_default_group_width_set(wmOperatorType *ot);

View File

@@ -65,6 +65,7 @@ void node_operatortypes()
WM_operatortype_append(NODE_OT_group_ungroup);
WM_operatortype_append(NODE_OT_group_separate);
WM_operatortype_append(NODE_OT_group_edit);
WM_operatortype_append(NODE_OT_group_enter_exit);
WM_operatortype_append(NODE_OT_default_group_width_set);

View File

@@ -135,7 +135,7 @@ static bool node_frame_select_isect_mouse(const SpaceNode &snode,
return false;
}
static bNode *node_under_mouse_select(const SpaceNode &snode, const float2 mouse)
bNode *node_under_mouse_get(const SpaceNode &snode, const float2 mouse)
{
for (bNode *node : tree_draw_order_calc_nodes_reversed(*snode.edittree)) {
switch (node->type_legacy) {
@@ -158,7 +158,7 @@ static bNode *node_under_mouse_select(const SpaceNode &snode, const float2 mouse
static bool is_position_over_node_or_socket(SpaceNode &snode, ARegion &region, const float2 &mouse)
{
if (node_under_mouse_select(snode, mouse)) {
if (node_under_mouse_get(snode, mouse)) {
return true;
}
if (node_find_indicated_socket(snode, region, mouse, SOCK_IN | SOCK_OUT)) {
@@ -606,7 +606,7 @@ static bool node_mouse_select(bContext *C,
if (!sock) {
/* Find the closest visible node. */
node = node_under_mouse_select(snode, cursor);
node = node_under_mouse_get(snode, cursor);
found = (node != nullptr);
node_was_selected = node && (node->flag & SELECT);