From 84b46d07d8a069655598859c819b503338704589 Mon Sep 17 00:00:00 2001 From: Leon Schittek Date: Sat, 3 Jun 2023 16:15:45 +0200 Subject: [PATCH 1/3] Node Editor: Improve working with nodes inside frames Improvements to the workflow of adding/removing nodes inside frames: * Always attach all nodes to frames that include them after each transform * Allow detaching nodes from their parent frame during transform by pressing the `Shift` key * Nodes can be added to/removed from a frame by resizing it This is is implementing the base functionality outlined in #106956. --- .../keyconfig/keymap_data/blender_default.py | 6 +- .../keymap_data/industry_compatible_data.py | 6 +- source/blender/editors/include/ED_node.hh | 2 + source/blender/editors/space_node/drawnode.cc | 6 - .../blender/editors/space_node/node_draw.cc | 65 +++++--- .../blender/editors/space_node/node_edit.cc | 33 +++- .../blender/editors/space_node/node_intern.hh | 1 - source/blender/editors/space_node/node_ops.cc | 4 - .../editors/space_node/node_relationships.cc | 154 ++++++------------ source/blender/editors/transform/transform.c | 65 ++++++-- source/blender/editors/transform/transform.h | 16 +- .../transform/transform_convert_node.cc | 97 +++++++++-- .../transform/transform_mode_translate.c | 2 +- source/blender/makesdna/DNA_node_types.h | 4 +- 14 files changed, 282 insertions(+), 179 deletions(-) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 3269111f391..af93aaf84a7 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -5943,8 +5943,10 @@ def km_transform_modal_map(params): ("AUTOIK_CHAIN_LEN_UP", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "shift": True}, None), ("AUTOIK_CHAIN_LEN_DOWN", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "shift": True}, None), ("INSERTOFS_TOGGLE_DIR", {"type": 'T', "value": 'PRESS'}, None), - ("NODE_ATTACH_ON", {"type": 'LEFT_ALT', "value": 'RELEASE', "any": True}, None), - ("NODE_ATTACH_OFF", {"type": 'LEFT_ALT', "value": 'PRESS', "any": True}, None), + ("NODE_LINK_ATTACH_ON", {"type": 'LEFT_ALT', "value": 'RELEASE', "any": True}, None), + ("NODE_LINK_ATTACH_OFF", {"type": 'LEFT_ALT', "value": 'PRESS', "any": True}, None), + ("NODE_FRAME_ATTACH_ON", {"type": 'LEFT_SHIFT', "value": 'PRESS', "any": True}, None), + ("NODE_FRAME_ATTACH_OFF", {"type": 'LEFT_SHIFT', "value": 'RELEASE', "any": True}, None), ("AUTOCONSTRAIN", {"type": 'MIDDLEMOUSE', "value": 'ANY', "alt": params.use_transform_navigation}, None), ("AUTOCONSTRAINPLANE", {"type": 'MIDDLEMOUSE', "value": 'ANY', "shift": True, "alt": params.use_transform_navigation}, None), ("PRECISION", {"type": 'LEFT_SHIFT', "value": 'ANY', "any": True}, None), diff --git a/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index d4794dc28b5..5ce6acd11e0 100644 --- a/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -4065,8 +4065,10 @@ def km_transform_modal_map(_params): ("AUTOIK_CHAIN_LEN_UP", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "shift": True}, None), ("AUTOIK_CHAIN_LEN_DOWN", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "shift": True}, None), ("INSERTOFS_TOGGLE_DIR", {"type": 'T', "value": 'PRESS'}, None), - ("NODE_ATTACH_ON", {"type": 'LEFT_ALT', "value": 'RELEASE', "any": True}, None), - ("NODE_ATTACH_OFF", {"type": 'LEFT_ALT', "value": 'PRESS', "any": True}, None), + ("NODE_LINK_ATTACH_ON", {"type": 'LEFT_ALT', "value": 'RELEASE', "any": True}, None), + ("NODE_LINK_ATTACH_OFF", {"type": 'LEFT_ALT', "value": 'PRESS', "any": True}, None), + ("NODE_FRAME_ATTACH_ON", {"type": 'LEFT_SHIFT', "value": 'PRESS', "any": True}, None), + ("NODE_FRAME_ATTACH_OFF", {"type": 'LEFT_SHIFT', "value": 'RELEASE', "any": True}, None), ("AUTOCONSTRAIN", {"type": 'MIDDLEMOUSE', "value": 'ANY'}, None), ("AUTOCONSTRAINPLANE", {"type": 'MIDDLEMOUSE', "value": 'ANY', "shift": True}, None), ("PRECISION", {"type": 'LEFT_SHIFT', "value": 'ANY', "any": True}, None), diff --git a/source/blender/editors/include/ED_node.hh b/source/blender/editors/include/ED_node.hh index bd8b504b0fd..51c688847f2 100644 --- a/source/blender/editors/include/ED_node.hh +++ b/source/blender/editors/include/ED_node.hh @@ -26,6 +26,8 @@ void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion ®ion); void node_insert_on_link_flags(Main &bmain, SpaceNode &snode); void node_insert_on_link_flags_clear(bNodeTree &node_tree); +void node_tree_update_frame_attachment(bNodeTree &ntree); + /** * Draw a single node socket at default size. * \note this is only called from external code, internally #node_socket_draw_nested() is used for diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 8d7cb6fa7a2..390dba0892b 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -229,12 +229,6 @@ NodeResizeDirection node_get_resize_direction(const SpaceNode &snode, const float size = NODE_RESIZE_MARGIN * math::max(snode.runtime->aspect, 1.0f); if (node->type == NODE_FRAME) { - NodeFrame *data = (NodeFrame *)node->storage; - - /* shrinking frame size is determined by child nodes */ - if (!(data->flag & NODE_FRAME_RESIZEABLE)) { - return NODE_RESIZE_NONE; - } NodeResizeDirection dir = NODE_RESIZE_NONE; diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index ddcc96c49f6..644e643aa17 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -257,6 +257,24 @@ static bool compare_node_depth(const bNode *a, const bNode *b) return false; } + /* Put frames in the back during resizing. Otherwise the bigger frame should be behind. */ + if (a->is_frame() && b->is_frame()) { + if (static_cast(a->storage)->flag & NODE_FRAME_RESIZING) { + return true; + } + if (static_cast(b->storage)->flag & NODE_FRAME_RESIZING) { + return false; + } + + rctf *rect_a = &a->runtime->totr; + rctf *rect_b = &b->runtime->totr; + + const float area_a = BLI_rctf_size_x(rect_a) * BLI_rctf_size_y(rect_a); + const float area_b = BLI_rctf_size_x(rect_b) * BLI_rctf_size_y(rect_b); + + return area_a > area_b; + } + /* One has a higher selection state (active > selected > nothing). */ if (a_active && !b_active) { return false; @@ -2748,31 +2766,34 @@ static void frame_node_prepare_for_draw(bNode &node, Span nodes) rctf rect; node_to_updated_rect(node, rect); - /* Frame can be resized manually only if shrinking is disabled or no children are attached. */ - data->flag |= NODE_FRAME_RESIZEABLE; + /* Shrinking is disabled while resizing the node. */ + const bool shrink = (data->flag & NODE_FRAME_SHRINK) && + ((data->flag & NODE_FRAME_RESIZING) == 0); + /* For shrinking bounding box, initialize the rect from first child node. */ - bool bbinit = (data->flag & NODE_FRAME_SHRINK); - /* Fit bounding box to all children. */ - for (const bNode *tnode : nodes) { - if (tnode->parent != &node) { - continue; - } + if (shrink) { + bool bbinit = true; + /* Fit bounding box to all children. */ + for (const bNode *tnode : nodes) { + if (tnode->parent != &node) { + continue; + } - /* Add margin to node rect. */ - rctf noderect = tnode->runtime->totr; - noderect.xmin -= margin; - noderect.xmax += margin; - noderect.ymin -= margin; - noderect.ymax += margin_top; + /* Add margin to node rect. */ + rctf noderect = tnode->runtime->totr; + noderect.xmin -= margin; + noderect.xmax += margin; + noderect.ymin -= margin; + noderect.ymax += margin_top; - /* First child initializes frame. */ - if (bbinit) { - bbinit = false; - rect = noderect; - data->flag &= ~NODE_FRAME_RESIZEABLE; - } - else { - BLI_rctf_union(&rect, &noderect); + /* First child initializes frame. */ + if (bbinit) { + bbinit = false; + rect = noderect; + } + else { + BLI_rctf_union(&rect, &noderect); + } } } diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 1a4ac74a3b5..291a0d253e3 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -892,10 +892,11 @@ static void node_resize_exit(bContext *C, wmOperator *op, bool cancel) { WM_cursor_modal_restore(CTX_wm_window(C)); + SpaceNode *snode = CTX_wm_space_node(C); + bNode *node = nodeGetActive(snode->edittree); + /* Restore old data on cancel. */ if (cancel) { - SpaceNode *snode = CTX_wm_space_node(C); - bNode *node = nodeGetActive(snode->edittree); NodeSizeWidget *nsw = (NodeSizeWidget *)op->customdata; node->locx = nsw->oldlocx; @@ -906,6 +907,15 @@ static void node_resize_exit(bContext *C, wmOperator *op, bool cancel) node->height = nsw->oldheight; } + if (node->is_frame()) { + NodeFrame *data = static_cast(node->storage); + data->flag &= ~NODE_FRAME_RESIZING; + } + + bNodeTree *node_tree = snode->edittree; + node_tree_update_frame_attachment(*node_tree); + ED_node_tree_propagate_change(C, CTX_data_main(C), node_tree); + MEM_freeN(op->customdata); op->customdata = nullptr; } @@ -1034,6 +1044,25 @@ static int node_resize_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } + if (node->is_frame()) { + NodeFrame *data = static_cast(node->storage); + data->flag |= NODE_FRAME_RESIZING; + + bNodeTree &node_tree = *snode->edittree; + node_sort(node_tree); + + /* Move child nodes up one level in the parenting hierarchy during resizing. */ + LISTBASE_FOREACH (bNode *, child_node, &node_tree.nodes) { + if (child_node->parent != node) { + continue; + } + nodeDetachNode(&node_tree, child_node); + if (node->parent) { + nodeAttachNode(&node_tree, child_node, node->parent); + } + } + } + node_resize_init(C, op, cursor, node, dir); return OPERATOR_RUNNING_MODAL; } diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 13ad0b6d613..3721c2b0c85 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -303,7 +303,6 @@ void NODE_OT_links_mute(wmOperatorType *ot); void NODE_OT_parent_set(wmOperatorType *ot); void NODE_OT_join(wmOperatorType *ot); -void NODE_OT_attach(wmOperatorType *ot); void NODE_OT_detach(wmOperatorType *ot); void NODE_OT_link_viewer(wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc index 4a5519a3e08..b3af2031a62 100644 --- a/source/blender/editors/space_node/node_ops.cc +++ b/source/blender/editors/space_node/node_ops.cc @@ -93,7 +93,6 @@ void node_operatortypes() WM_operatortype_append(NODE_OT_parent_set); WM_operatortype_append(NODE_OT_join); - WM_operatortype_append(NODE_OT_attach); WM_operatortype_append(NODE_OT_detach); WM_operatortype_append(NODE_OT_clipboard_copy); @@ -149,7 +148,6 @@ void ED_operatormacros_node() "Move nodes and attach to frame", OPTYPE_UNDO | OPTYPE_REGISTER); mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); - WM_operatortype_macro_define(ot, "NODE_OT_attach"); /* NODE_OT_translate_attach with remove_on_cancel set to true. */ ot = WM_operatortype_append_macro("NODE_OT_translate_attach_remove_on_cancel", @@ -159,7 +157,6 @@ void ED_operatormacros_node() mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); RNA_boolean_set(mot->ptr, "remove_on_cancel", true); RNA_boolean_set(mot->ptr, "view2d_edge_pan", true); - WM_operatortype_macro_define(ot, "NODE_OT_attach"); /* NOTE: Currently not in a default keymap or menu due to messy keymaps * and tricky invoke functionality. @@ -171,7 +168,6 @@ void ED_operatormacros_node() OPTYPE_UNDO | OPTYPE_REGISTER); WM_operatortype_macro_define(ot, "NODE_OT_detach"); mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); - WM_operatortype_macro_define(ot, "NODE_OT_attach"); ot = WM_operatortype_append_macro("NODE_OT_duplicate_move", "Duplicate", diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 1c78d1e8856..a1b56e4d8ee 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -1885,83 +1885,66 @@ void NODE_OT_join(wmOperatorType *ot) /** \name Attach Operator * \{ */ -static bNode *node_find_frame_to_attach(ARegion ®ion, - const bNodeTree &ntree, - const int2 mouse_xy) +static bool node_can_be_attached_to_frame(const bNode &node, const bNode &frame) { - /* convert mouse coordinates to v2d space */ - float2 cursor; - UI_view2d_region_to_view(®ion.v2d, mouse_xy.x, mouse_xy.y, &cursor.x, &cursor.y); + if (&node == &frame) { + return false; + } + if (node.parent == nullptr) { + return true; + } + + /* Check if the direct parent of the node is also an ancestor of the frame. */ + for (bNode *frame_ancestor = frame.parent; frame_ancestor; + frame_ancestor = frame_ancestor->parent) { + if (frame_ancestor == node.parent) { + return true; + } + } + + return false; +} + +static bool node_is_inside_parent_frame(const bNode &node) +{ + if (node.parent == nullptr) { + return false; + } + + rctf *node_rect = &node.runtime->totr; + rctf *frame_rect = &node.parent->runtime->totr; + return BLI_rctf_inside_rctf(frame_rect, node_rect); +} + +static void node_attach_to_underlying_frame(bNodeTree &ntree, bNode &node) +{ + rctf *node_rect = &node.runtime->totr; LISTBASE_FOREACH_BACKWARD (bNode *, frame, &ntree.nodes) { - /* skip selected, those are the nodes we want to attach */ - if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) { + if (!frame->is_frame()) { continue; } - if (BLI_rctf_isect_pt_v(&frame->runtime->totr, cursor)) { - return frame; + if (!node_can_be_attached_to_frame(node, *frame)) { + continue; + } + rctf *frame_rect = &frame->runtime->totr; + if (BLI_rctf_inside_rctf(frame_rect, node_rect)) { + nodeAttachNode(&ntree, &node, frame); } } - - return nullptr; } -static int node_attach_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event) +void node_tree_update_frame_attachment(bNodeTree &ntree) { - ARegion ®ion = *CTX_wm_region(C); - SpaceNode &snode = *CTX_wm_space_node(C); - bNodeTree &ntree = *snode.edittree; - bNode *frame = node_find_frame_to_attach(region, ntree, event->mval); - if (frame == nullptr) { - /* Return "finished" so that auto offset operator macros can work. */ - return OPERATOR_FINISHED; + LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { + if (!node_is_inside_parent_frame(*node)) { + nodeDetachNode(&ntree, node); + } } - LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree.nodes) { - if (!(node->flag & NODE_SELECT)) { - continue; - } - - /* Disallow moving a parent into its child. */ - if (node->is_frame() && nodeIsParentAndChild(node, frame)) { - continue; - } - - if (node->parent == nullptr) { - nodeAttachNode(&ntree, node, frame); - continue; - } - - /* Attach nodes which share parent with the frame. */ - const bool share_parent = nodeIsParentAndChild(node->parent, frame); - if (!share_parent) { - continue; - } - - nodeDetachNode(&ntree, node); - nodeAttachNode(&ntree, node, frame); + LISTBASE_FOREACH (bNode *, frame, &ntree.nodes) { + node_attach_to_underlying_frame(ntree, *frame); } - - node_sort(ntree); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); - - return OPERATOR_FINISHED; -} - -void NODE_OT_attach(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Attach Nodes"; - ot->description = "Attach active node to a frame"; - ot->idname = "NODE_OT_attach"; - - /* api callbacks */ - - ot->invoke = node_attach_invoke; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /** \} */ @@ -2414,8 +2397,7 @@ static bool node_link_insert_offset_chain_cb(bNode *fromnode, } static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd, - ARegion *region, - const int mouse_xy[2], + const SpaceNode &snode, const bool right_alignment) { bNodeTree *ntree = iofsd->ntree; @@ -2438,44 +2420,6 @@ static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd, rctf totr_insert; node_to_updated_rect(insert, totr_insert); - /* Frame attachment wasn't handled yet so we search the frame that the node will be attached to - * later. */ - insert.parent = node_find_frame_to_attach(*region, *ntree, mouse_xy); - - /* This makes sure nodes are also correctly offset when inserting a node on top of a frame - * without actually making it a part of the frame (because mouse isn't intersecting it) - * - logic here is similar to node_find_frame_to_attach. */ - if (!insert.parent || - (prev->parent && (prev->parent == next->parent) && (prev->parent != insert.parent))) - { - bNode *frame; - rctf totr_frame; - - /* check nodes front to back */ - for (frame = (bNode *)ntree->nodes.last; frame; frame = frame->prev) { - /* skip selected, those are the nodes we want to attach */ - if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) { - continue; - } - - /* for some reason frame y coords aren't correct yet */ - node_to_updated_rect(*frame, totr_frame); - - if (BLI_rctf_isect_x(&totr_frame, totr_insert.xmin) && - BLI_rctf_isect_x(&totr_frame, totr_insert.xmax)) - { - if (BLI_rctf_isect_y(&totr_frame, totr_insert.ymin) || - BLI_rctf_isect_y(&totr_frame, totr_insert.ymax)) - { - /* frame isn't insert.parent actually, but this is needed to make offsetting - * nodes work correctly for above checked cases (it is restored later) */ - insert.parent = frame; - break; - } - } - } - } - /* *** ensure offset at the left (or right for right_alignment case) of insert_node *** */ float dist = right_alignment ? totr_insert.xmin - prev->runtime->totr.xmax : @@ -2609,7 +2553,7 @@ static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent iofsd->anim_timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.02); node_link_insert_offset_ntree( - iofsd, CTX_wm_region(C), event->mval, (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT)); + iofsd, *snode, (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT)); /* add temp handler */ WM_event_add_modal_handler(C, op); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 69d7bec6465..c2261cce1d1 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -616,8 +616,10 @@ static bool transform_modal_item_poll(const wmOperator *op, int value) break; } case TFM_MODAL_INSERTOFS_TOGGLE_DIR: - case TFM_MODAL_NODE_ATTACH_ON: - case TFM_MODAL_NODE_ATTACH_OFF: { + case TFM_MODAL_NODE_LINK_ATTACH_ON: + case TFM_MODAL_NODE_LINK_ATTACH_OFF: + case TFM_MODAL_NODE_FRAME_ATTACH_ON: + case TFM_MODAL_NODE_FRAME_ATTACH_OFF: { if (t->spacetype != SPACE_NODE) { return false; } @@ -741,8 +743,18 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf) 0, "Toggle Direction for Node Auto-Offset", ""}, - {TFM_MODAL_NODE_ATTACH_ON, "NODE_ATTACH_ON", 0, "Node Attachment", ""}, - {TFM_MODAL_NODE_ATTACH_OFF, "NODE_ATTACH_OFF", 0, "Node Attachment (Off)", ""}, + {TFM_MODAL_NODE_LINK_ATTACH_ON, "NODE_LINK_ATTACH_ON", 0, "Node Link Attachment", ""}, + {TFM_MODAL_NODE_LINK_ATTACH_OFF, + "NODE_LINK_ATTACH_OFF", + 0, + "Node Link Attachment (Off)", + ""}, + {TFM_MODAL_NODE_FRAME_ATTACH_ON, "NODE_FRAME_ATTACH_ON", 0, "Node Frame Attachment", ""}, + {TFM_MODAL_NODE_FRAME_ATTACH_OFF, + "NODE_FRAME_ATTACH_OFF", + 0, + "Node Frame Attachment (Off)", + ""}, {TFM_MODAL_TRANSLATE, "TRANSLATE", 0, "Move", ""}, {TFM_MODAL_VERT_EDGE_SLIDE, "VERT_EDGE_SLIDE", 0, "Vert/Edge Slide", ""}, {TFM_MODAL_ROTATE, "ROTATE", 0, "Rotate", ""}, @@ -1204,13 +1216,23 @@ int transformEvent(TransInfo *t, const wmEvent *event) t->redraw |= TREDRAW_SOFT; } break; - case TFM_MODAL_NODE_ATTACH_ON: - t->modifiers |= MOD_NODE_ATTACH; + case TFM_MODAL_NODE_LINK_ATTACH_ON: + t->modifiers |= MOD_NODE_LINK_ATTACH; t->redraw |= TREDRAW_HARD; handled = true; break; - case TFM_MODAL_NODE_ATTACH_OFF: - t->modifiers &= ~MOD_NODE_ATTACH; + case TFM_MODAL_NODE_LINK_ATTACH_OFF: + t->modifiers &= ~MOD_NODE_LINK_ATTACH; + t->redraw |= TREDRAW_HARD; + handled = true; + break; + case TFM_MODAL_NODE_FRAME_ATTACH_ON: + t->modifiers |= MOD_NODE_FRAME_ATTACH; + t->redraw |= TREDRAW_HARD; + handled = true; + break; + case TFM_MODAL_NODE_FRAME_ATTACH_OFF: + t->modifiers &= ~MOD_NODE_FRAME_ATTACH; t->redraw |= TREDRAW_HARD; handled = true; break; @@ -1967,13 +1989,13 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve if (t->data_type == &TransConvertType_Node) { /* Set the initial auto-attach flag based on whether the chosen keymap key is pressed at the * start of the operator. */ - t->modifiers |= MOD_NODE_ATTACH; + t->modifiers &= ~MOD_NODE_LINK_ATTACH; LISTBASE_FOREACH (const wmKeyMapItem *, kmi, &t->keymap->items) { if (kmi->flag & KMI_INACTIVE) { continue; } - if (kmi->propvalue == TFM_MODAL_NODE_ATTACH_OFF && kmi->val == KM_PRESS) { + if (kmi->propvalue == TFM_MODAL_NODE_LINK_ATTACH_ON && kmi->val == KM_PRESS) { if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && (event->modifier & KM_CTRL)) || (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && @@ -1981,7 +2003,28 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && (event->modifier & KM_ALT)) || ((kmi->type == EVT_OSKEY) && (event->modifier & KM_OSKEY))) { - t->modifiers &= ~MOD_NODE_ATTACH; + t->modifiers |= MOD_NODE_LINK_ATTACH; + } + break; + } + } + + /* Set the initial frame-attach flag based on whether the chosen keymap key is pressed at the + * start of the operator. */ + t->modifiers &= ~MOD_NODE_FRAME_ATTACH; + LISTBASE_FOREACH (const wmKeyMapItem *, kmi, &t->keymap->items) { + if (kmi->flag & KMI_INACTIVE) { + continue; + } + if (kmi->propvalue == TFM_MODAL_NODE_LINK_ATTACH_ON && kmi->val == KM_PRESS) { + if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && + (event->modifier & KM_CTRL)) || + (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && + (event->modifier & KM_SHIFT)) || + (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && (event->modifier & KM_ALT)) || + ((kmi->type == EVT_OSKEY) && (event->modifier & KM_OSKEY))) + { + t->modifiers |= MOD_NODE_FRAME_ATTACH; } break; } diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index e49999c00a5..a3eda9687ed 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -156,11 +156,12 @@ typedef enum { MOD_SNAP = 1 << 2, MOD_SNAP_INVERT = 1 << 3, MOD_CONSTRAINT_SELECT_PLANE = 1 << 4, - MOD_NODE_ATTACH = 1 << 5, - MOD_SNAP_FORCED = 1 << 6, - MOD_EDIT_SNAP_SOURCE = 1 << 7, + MOD_NODE_LINK_ATTACH = 1 << 5, + MOD_NODE_FRAME_ATTACH = 1 << 6, + MOD_SNAP_FORCED = 1 << 7, + MOD_EDIT_SNAP_SOURCE = 1 << 8, } eTModifier; -ENUM_OPERATORS(eTModifier, MOD_NODE_ATTACH) +ENUM_OPERATORS(eTModifier, MOD_EDIT_SNAP_SOURCE) /** #TransSnap.status */ typedef enum eTSnap { @@ -256,8 +257,8 @@ enum { TFM_MODAL_AUTOIK_LEN_INC = 22, TFM_MODAL_AUTOIK_LEN_DEC = 23, - TFM_MODAL_NODE_ATTACH_ON = 24, - TFM_MODAL_NODE_ATTACH_OFF = 25, + TFM_MODAL_NODE_LINK_ATTACH_ON = 24, + TFM_MODAL_NODE_LINK_ATTACH_OFF = 25, /** For analog input, like track-pad. */ TFM_MODAL_PROPSIZE = 26, @@ -275,6 +276,9 @@ enum { TFM_MODAL_EDIT_SNAP_SOURCE_ON = 34, TFM_MODAL_EDIT_SNAP_SOURCE_OFF = 35, + + TFM_MODAL_NODE_FRAME_ATTACH_ON = 36, + TFM_MODAL_NODE_FRAME_ATTACH_OFF = 37, }; /** \} */ diff --git a/source/blender/editors/transform/transform_convert_node.cc b/source/blender/editors/transform/transform_convert_node.cc index 4456e5ad19f..03fce0e0759 100644 --- a/source/blender/editors/transform/transform_convert_node.cc +++ b/source/blender/editors/transform/transform_convert_node.cc @@ -31,26 +31,31 @@ #include "WM_api.h" -struct TransCustomDataNode { +struct TransInfoCustomDataNode { View2DEdgePanData edgepan_data; /* Compare if the view has changed so we can update with `transformViewUpdate`. */ rctf viewrect_prev; + eTModifier attachment_state; +}; + +struct TransCustomDataNode { + /* For reversible unparenting during transform. */ + bNode *parent; }; /* -------------------------------------------------------------------- */ /** \name Node Transform Creation * \{ */ -static void create_transform_data_for_node(TransData &td, - TransData2D &td2d, - bNode &node, - const float dpi_fac) +static void create_transform_data_for_node( + TransData &td, TransData2D &td2d, TransCustomDataNode &tdc, bNode &node, const float dpi_fac) { float locx, locy; /* account for parents (nested nodes) */ if (node.parent) { + tdc.parent = node.parent; blender::bke::nodeToView(node.parent, node.locx + roundf(node.offsetx), node.locy + roundf(node.offsety), @@ -101,6 +106,40 @@ static bool is_node_parent_select(const bNode *node) return false; } +static void node_transform_restore_parenting_hierarchy(TransDataContainer *tc, + bNodeTree &node_tree) +{ + for (int i = 0; i < tc->data_len; i++) { + TransData *td = &tc->data[i]; + bNode *node = static_cast(td->extra); + + TransCustomDataNode *tdc = static_cast(tc->custom.type.data); + bNode *parent_node = tdc[i].parent; + + if (parent_node == nullptr) { + continue; + } + + nodeAttachNode(&node_tree, node, parent_node); + } +} + +static void node_transform_detach_nodes(bNodeTree &node_tree) +{ + for (bNode *node : blender::ed::space_node::get_selected_nodes(node_tree)) { + if (node->parent == nullptr) { + continue; + } + if (is_node_parent_select(node)) { + /* When a parent frame is transformed together with the node we don't need to clear + * the parenting. */ + continue; + } + + nodeDetachNode(&node_tree, node); + } +} + static void createTransNodeData(bContext * /*C*/, TransInfo *t) { using namespace blender; @@ -112,7 +151,7 @@ static void createTransNodeData(bContext * /*C*/, TransInfo *t) } /* Custom data to enable edge panning during the node transform */ - TransCustomDataNode *customdata = MEM_cnew(__func__); + TransInfoCustomDataNode *customdata = MEM_cnew(__func__); UI_view2d_edge_pan_init(t->context, &customdata->edgepan_data, NODE_EDGE_PAN_INSIDE_PAD, @@ -123,11 +162,15 @@ 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) { + if (t->modifiers & MOD_NODE_LINK_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_clear(*node_tree); + } + + if (t->modifiers & MOD_NODE_FRAME_ATTACH) { + node_transform_detach_nodes(*node_tree); } t->custom.type.data = customdata; @@ -147,9 +190,13 @@ static void createTransNodeData(bContext * /*C*/, TransInfo *t) tc->data_len = nodes.size(); tc->data = MEM_cnew_array(tc->data_len, __func__); tc->data_2d = MEM_cnew_array(tc->data_len, __func__); + tc->custom.type.data = MEM_cnew_array(tc->data_len, __func__); + tc->custom.type.use_free = true; for (const int i : nodes.index_range()) { - create_transform_data_for_node(tc->data[i], tc->data_2d[i], *nodes[i], UI_SCALE_FAC); + TransCustomDataNode *data_node = static_cast(tc->custom.type.data); + create_transform_data_for_node( + tc->data[i], tc->data_2d[i], data_node[i], *nodes[i], UI_SCALE_FAC); } } @@ -207,8 +254,9 @@ static void flushTransNodes(TransInfo *t) using namespace blender::ed; const float dpi_fac = UI_SCALE_FAC; SpaceNode *snode = static_cast(t->area->spacedata.first); + bNodeTree &node_tree = *snode->edittree; - TransCustomDataNode *customdata = (TransCustomDataNode *)t->custom.type.data; + TransInfoCustomDataNode *customdata = (TransInfoCustomDataNode *)t->custom.type.data; if (t->options & CTX_VIEW2D_EDGE_PAN) { if (t->state == TRANS_CANCEL) { @@ -264,13 +312,31 @@ static void flushTransNodes(TransInfo *t) } } - /* handle intersection with noodles */ - if (tc->data_len == 1) { - if (t->modifiers & MOD_NODE_ATTACH) { + /* Handle intersection with node links. */ + if (t->modifiers & MOD_NODE_LINK_ATTACH) { + if (tc->data_len == 1) { space_node::node_insert_on_link_flags_set(*snode, *t->region); } + } + else { + if (tc->data_len == 1) { + space_node::node_insert_on_link_flags_clear(node_tree); + } + } + + /* Handle detaching nodes from parent frames. */ + const bool frame_attachment_state_changed = (t->modifiers & MOD_NODE_FRAME_ATTACH) != + (customdata->attachment_state & + MOD_NODE_FRAME_ATTACH); + + if (frame_attachment_state_changed) { + if (t->modifiers & MOD_NODE_FRAME_ATTACH) { + node_transform_detach_nodes(node_tree); + customdata->attachment_state = MOD_NODE_FRAME_ATTACH; + } else { - space_node::node_insert_on_link_flags_clear(*snode->edittree); + node_transform_restore_parenting_hierarchy(tc, node_tree); + customdata->attachment_state &= ~MOD_NODE_FRAME_ATTACH; } } } @@ -305,12 +371,13 @@ static void special_aftertrans_update__node(bContext *C, TransInfo *t) if (!canceled) { ED_node_post_apply_transform(C, snode->edittree); - if (t->modifiers & MOD_NODE_ATTACH) { + if (t->modifiers & MOD_NODE_LINK_ATTACH) { space_node::node_insert_on_link_flags(*bmain, *snode); } } space_node::node_insert_on_link_flags_clear(*ntree); + space_node::node_tree_update_frame_attachment(*ntree); wmOperatorType *ot = WM_operatortype_find("NODE_OT_insert_offset", true); BLI_assert(ot); diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index d8d8f2d718a..cdb9ad6defa 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -315,7 +315,7 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ char str_attach_km[64]; WM_modalkeymap_items_to_string( - t->keymap, TFM_MODAL_NODE_ATTACH_OFF, true, str_attach_km, sizeof(str_attach_km)); + t->keymap, TFM_MODAL_NODE_LINK_ATTACH_OFF, true, str_attach_km, sizeof(str_attach_km)); ofs += BLI_snprintf_rlen( str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(", %s: Toggle auto-attach"), str_attach_km); } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 8a7f5d45d09..9311286fbfe 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1678,8 +1678,8 @@ typedef struct NodeShaderMix { /* Frame node flags. */ -#define NODE_FRAME_SHRINK 1 /* keep the bounding box minimal */ -#define NODE_FRAME_RESIZEABLE 2 /* test flag, if frame can be resized by user */ +#define NODE_FRAME_SHRINK 1 /* keep the bounding box minimal */ +#define NODE_FRAME_RESIZING 2 /* temporary flag for when the frame is resized by user */ /* Proxy node flags. */ -- 2.30.2 From 30ccfbe006717c18f53569c610cebf532c44a3f1 Mon Sep 17 00:00:00 2001 From: Leon Schittek Date: Sat, 10 Jun 2023 09:56:05 +0200 Subject: [PATCH 2/3] Fix link insertion flag not being properly set when starting transform. --- source/blender/editors/transform/transform.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index c2261cce1d1..12d3d17deb9 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1989,13 +1989,13 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve if (t->data_type == &TransConvertType_Node) { /* Set the initial auto-attach flag based on whether the chosen keymap key is pressed at the * start of the operator. */ - t->modifiers &= ~MOD_NODE_LINK_ATTACH; + t->modifiers |= MOD_NODE_LINK_ATTACH; LISTBASE_FOREACH (const wmKeyMapItem *, kmi, &t->keymap->items) { if (kmi->flag & KMI_INACTIVE) { continue; } - if (kmi->propvalue == TFM_MODAL_NODE_LINK_ATTACH_ON && kmi->val == KM_PRESS) { + if (kmi->propvalue == TFM_MODAL_NODE_LINK_ATTACH_OFF && kmi->val == KM_PRESS) { if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && (event->modifier & KM_CTRL)) || (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && @@ -2003,7 +2003,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && (event->modifier & KM_ALT)) || ((kmi->type == EVT_OSKEY) && (event->modifier & KM_OSKEY))) { - t->modifiers |= MOD_NODE_LINK_ATTACH; + t->modifiers &= ~MOD_NODE_LINK_ATTACH; } break; } -- 2.30.2 From 6c85b27b94ce3ce4ffb7b0ac3bfa881714dd337a Mon Sep 17 00:00:00 2001 From: Leon Schittek Date: Sat, 17 Jun 2023 19:34:16 +0200 Subject: [PATCH 3/3] Remove unnecessary check. --- source/blender/editors/transform/transform_convert_node.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/editors/transform/transform_convert_node.cc b/source/blender/editors/transform/transform_convert_node.cc index 5626c182af3..2057239ddfe 100644 --- a/source/blender/editors/transform/transform_convert_node.cc +++ b/source/blender/editors/transform/transform_convert_node.cc @@ -302,9 +302,7 @@ static void flushTransNodes(TransInfo *t) } } else { - if (tc->data_len == 1) { space_node::node_insert_on_link_flags_clear(node_tree); - } } /* Handle detaching nodes from parent frames. */ -- 2.30.2