1
1

Compare commits

...

8 Commits

Author SHA1 Message Date
51bc509d62 Use the new dissolve function for all operators. 2021-07-18 08:51:06 +01:00
727b500465 Improved link dissolve method that supports multiple nodes.
The new functions keep links between the selected nodes intact when
disconnecting them from their surroundings.
2021-07-17 18:39:09 +01:00
65c13b0243 Removed the NODE_OT_duplicate_move_keep_inputs operator.
This is a variation of NODE_OT_duplicate_move with the "keep_inputs"
property set. This can be done in keymaps instead.
2021-07-17 08:22:40 +01:00
90f986cee4 Ensure that link hilites are cleared when the alt key is pressed. 2021-07-17 08:04:14 +01:00
a8327542c5 Removed the NODE_OT_translate_attach_remove_on_cancel operator.
This is just a variation of NODE_OT_translate_attach with the
"remove_on_cancel" property set. This can be done from keymaps instead.
2021-07-17 08:02:57 +01:00
8b4052e063 Use a modal keymap entry to toggle node unlinking with ALT. 2021-07-16 19:33:20 +01:00
2b43df378c Add operator properties to transform for toggling node link intersect. 2021-07-16 16:36:51 +01:00
65509a4a05 Rename the ED_node_link_intersect_test function for clarity. 2021-07-16 09:43:12 +01:00
13 changed files with 314 additions and 90 deletions

View File

@@ -1865,7 +1865,8 @@ def km_node_editor(params):
{"properties": [("replace", True)]}),
op_menu("NODE_MT_add", {"type": 'A', "value": 'PRESS', "shift": True}),
("node.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None),
("node.duplicate_move_keep_inputs", {"type": 'D', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("node.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True, "ctrl": True},
{"properties": [("NODE_OT_duplicate", [("keep_inputs", True)])]}),
("node.parent_set", {"type": 'P', "value": 'PRESS', "ctrl": True}, None),
("node.detach", {"type": 'P', "value": 'PRESS', "alt": True}, None),
("node.join", {"type": 'J', "value": 'PRESS', "ctrl": True}, None),
@@ -1910,6 +1911,12 @@ def km_node_editor(params):
("node.translate_attach", {"type": 'G', "value": 'PRESS'}, None),
("node.translate_attach", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
("node.translate_attach", {"type": params.select_tweak, "value": 'ANY'}, None),
("node.translate_attach", {"type": 'G', "value": 'PRESS', "alt": True},
{"properties": [("TRANSFORM_OT_translate", [("node_unlink", True)])]}),
("node.translate_attach", {"type": 'EVT_TWEAK_L', "value": 'ANY', "alt": True},
{"properties": [("TRANSFORM_OT_translate", [("node_unlink", True)])]}),
("node.translate_attach", {"type": params.select_tweak, "value": 'ANY', "alt": True},
{"properties": [("TRANSFORM_OT_translate", [("node_unlink", True)])]}),
("transform.translate", {"type": 'G', "value": 'PRESS'}, None),
("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
{"properties": [("release_confirm", True)]}),
@@ -1917,9 +1924,6 @@ def km_node_editor(params):
{"properties": [("release_confirm", True)]}),
("transform.rotate", {"type": 'R', "value": 'PRESS'}, None),
("transform.resize", {"type": 'S', "value": 'PRESS'}, None),
("node.move_detach_links", {"type": 'D', "value": 'PRESS', "alt": True}, None),
("node.move_detach_links_release", {"type": params.action_tweak, "value": 'ANY', "alt": True}, None),
("node.move_detach_links", {"type": params.select_tweak, "value": 'ANY', "alt": True}, None),
("wm.context_toggle", {"type": 'TAB', "value": 'PRESS', "shift": True},
{"properties": [("data_path", 'tool_settings.use_snap')]}),
("wm.context_menu_enum", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True},
@@ -5182,6 +5186,8 @@ 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_UNLINK_ON", {"type": 'LEFT_ALT', "value": 'PRESS', "any": True}, None),
("NODE_UNLINK_OFF", {"type": 'LEFT_ALT', "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),

View File

@@ -139,7 +139,7 @@ class NodeAddOperator:
if self.use_transform and ('FINISHED' in result):
# removes the node again if transform is canceled
bpy.ops.node.translate_attach_remove_on_cancel('INVOKE_DEFAULT')
bpy.ops.node.translate_attach('INVOKE_DEFAULT', TRANSFORM_OT_translate={"remove_on_cancel" : True})
return result
@@ -241,9 +241,8 @@ class NODE_OT_add_search(NodeAddOperator, Operator):
self.create_node(context, item.nodetype)
if self.use_transform:
bpy.ops.node.translate_attach_remove_on_cancel(
'INVOKE_DEFAULT')
props = bpy.ops.node.translate_attach(
'INVOKE_DEFAULT', TRANSFORM_OT_translate={"remove_on_cancel" : True})
return {'FINISHED'}
else:
return {'CANCELLED'}

View File

@@ -2357,6 +2357,10 @@ bNodeLink *nodeAddLink(
link->tonode = fromnode;
link->tosock = fromsock;
}
else {
/* Should never happen, incorrect socket in_out types. */
BLI_assert_unreachable();
}
if (ntree) {
ntree->update |= NTREE_UPDATE_LINKS;

View File

@@ -56,6 +56,18 @@ typedef enum {
#define NODE_EDGE_PAN_MAX_SPEED 40 /* In UI units per second, slower than default. */
#define NODE_EDGE_PAN_DELAY 1.0f
/* Result of the dissolve operation on node links.
* Note: this data becomes invalid if nodes or links are removed from the tree.
*/
typedef struct NodeEditDissolveLinksData {
/* Links that have been removed (not part of the tree any longer). */
struct bNodeLink *removed_links;
/* Links that have been added to replace old links. */
struct bNodeLink **added_links;
int tot_removed_links;
int tot_added_links;
} NodeEditDissolveLinksData;
/* space_node.c */
void ED_node_cursor_location_get(const struct SpaceNode *snode, float value[2]);
@@ -98,8 +110,17 @@ void ED_node_sort(struct bNodeTree *ntree);
float ED_node_grid_size(void);
/* node_relationships.c */
void ED_node_link_intersect_test(struct ScrArea *area, int test);
void ED_node_link_insert(struct Main *bmain, struct ScrArea *area);
void ED_node_link_hilite_clear(struct ScrArea *area);
void ED_node_link_hilite_intersected(struct ScrArea *area);
void ED_node_link_hilite_insert(struct Main *bmain, struct ScrArea *area);
void ED_node_dissolve_links(struct Main *bmain,
struct bNodeTree *ntree,
struct NodeEditDissolveLinksData *data);
void ED_node_dissolve_undo(struct Main *bmain,
struct bNodeTree *ntree,
const struct NodeEditDissolveLinksData *data);
void ED_node_dissolve_free_data(struct NodeEditDissolveLinksData *data);
/* node_edit.c */
void ED_node_set_tree_type(struct SpaceNode *snode, struct bNodeTreeType *typeinfo);

View File

@@ -137,6 +137,7 @@ int BIF_countTransformOrientation(const struct bContext *C);
#define P_GPENCIL_EDIT (1 << 13)
#define P_CURSOR_EDIT (1 << 14)
#define P_CLNOR_INVALIDATE (1 << 15)
#define P_NODES (1 << 16)
/* For properties performed when confirming the transformation. */
#define P_POST_TRANSFORM (1 << 19)

View File

@@ -1876,9 +1876,10 @@ static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op))
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_node_dissolve_links(bmain, snode->edittree, NULL);
LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
nodeInternalRelink(snode->edittree, node);
nodeRemoveNode(bmain, snode->edittree, node, true);
}
}

View File

@@ -148,16 +148,6 @@ void ED_operatormacros_node(void)
WM_operatortype_macro_define(ot, "NODE_OT_attach");
WM_operatortype_macro_define(ot, "NODE_OT_insert_offset");
/* NODE_OT_translate_attach with remove_on_canel set to true */
ot = WM_operatortype_append_macro("NODE_OT_translate_attach_remove_on_cancel",
"Move and Attach",
"Move nodes and attach to frame",
OPTYPE_UNDO | OPTYPE_REGISTER);
mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
RNA_boolean_set(mot->ptr, "remove_on_cancel", true);
WM_operatortype_macro_define(ot, "NODE_OT_attach");
WM_operatortype_macro_define(ot, "NODE_OT_insert_offset");
/* NOTE: Currently not in a default keymap or menu due to messy keymaps
* and tricky invoke functionality.
* Kept around in case users want to make own shortcuts.
@@ -176,30 +166,6 @@ void ED_operatormacros_node(void)
OPTYPE_UNDO | OPTYPE_REGISTER);
WM_operatortype_macro_define(ot, "NODE_OT_duplicate");
WM_operatortype_macro_define(ot, "NODE_OT_translate_attach");
/* modified operator call for duplicating with input links */
ot = WM_operatortype_append_macro("NODE_OT_duplicate_move_keep_inputs",
"Duplicate",
"Duplicate selected nodes keeping input links and move them",
OPTYPE_UNDO | OPTYPE_REGISTER);
mot = WM_operatortype_macro_define(ot, "NODE_OT_duplicate");
RNA_boolean_set(mot->ptr, "keep_inputs", true);
WM_operatortype_macro_define(ot, "NODE_OT_translate_attach");
ot = WM_operatortype_append_macro("NODE_OT_move_detach_links",
"Detach",
"Move a node to detach links",
OPTYPE_UNDO | OPTYPE_REGISTER);
WM_operatortype_macro_define(ot, "NODE_OT_links_detach");
WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
WM_operatortype_macro_define(ot, "NODE_OT_insert_offset");
ot = WM_operatortype_append_macro("NODE_OT_move_detach_links_release",
"Detach",
"Move a node to detach links",
OPTYPE_UNDO | OPTYPE_REGISTER);
WM_operatortype_macro_define(ot, "NODE_OT_links_detach");
WM_operatortype_macro_define(ot, "NODE_OT_translate_attach");
}
void node_keymap(struct wmKeyConfig *keyconf)

View File

@@ -29,6 +29,10 @@
#include "BLI_blenlib.h"
#include "BLI_easing.h"
#include "BLI_math.h"
#include "BLI_multi_value_map.hh"
#include "BLI_set.hh"
#include "BLI_stack.hh"
#include "BLI_vector.hh"
#include "BKE_anim_data.h"
#include "BKE_context.h"
@@ -1515,11 +1519,7 @@ static int detach_links_exec(bContext *C, wmOperator *UNUSED(op))
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
nodeInternalRelink(ntree, node);
}
}
ED_node_dissolve_links(CTX_data_main(C), ntree, NULL);
ntreeUpdateTree(CTX_data_main(C), ntree);
@@ -1855,10 +1855,7 @@ void NODE_OT_detach(wmOperatorType *ot)
* \{ */
/* prevent duplicate testing code below */
static bool ed_node_link_conditions(ScrArea *area,
bool test,
SpaceNode **r_snode,
bNode **r_select)
static bool ed_node_link_conditions(ScrArea *area, SpaceNode **r_snode, bNode **r_select)
{
SpaceNode *snode = area ? (SpaceNode *)area->spacedata.first : nullptr;
@@ -1870,11 +1867,6 @@ static bool ed_node_link_conditions(ScrArea *area,
return false;
}
if (!test) {
/* no need to look for a node */
return true;
}
bNode *node;
bNode *select = nullptr;
for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
@@ -1912,12 +1904,25 @@ static bool ed_node_link_conditions(ScrArea *area,
return true;
}
/* test == 0, clear all intersect flags */
void ED_node_link_intersect_test(ScrArea *area, int test)
/* Clear NODE_LINKFLAG_HILITE flags. */
void ED_node_link_hilite_clear(ScrArea *area)
{
if (area == nullptr || area->spacetype != SPACE_NODE) {
return;
}
SpaceNode *snode = (SpaceNode *)area->spacedata.first;
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
link->flag &= ~NODE_LINKFLAG_HILITE;
}
}
/* Sets NODE_LINKFLAG_HILITE on all links intersected by selected nodes. */
void ED_node_link_hilite_intersected(ScrArea *area)
{
bNode *select;
SpaceNode *snode;
if (!ed_node_link_conditions(area, test, &snode, &select)) {
if (!ed_node_link_conditions(area, &snode, &select)) {
return;
}
@@ -1926,10 +1931,6 @@ void ED_node_link_intersect_test(ScrArea *area, int test)
link->flag &= ~NODE_LINKFLAG_HILITE;
}
if (test == 0) {
return;
}
ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
/* find link to select/highlight */
@@ -2371,11 +2372,11 @@ void NODE_OT_insert_offset(wmOperatorType *ot)
* \{ */
/* assumes link with NODE_LINKFLAG_HILITE set */
void ED_node_link_insert(Main *bmain, ScrArea *area)
void ED_node_link_hilite_insert(Main *bmain, ScrArea *area)
{
bNode *select;
SpaceNode *snode;
if (!ed_node_link_conditions(area, true, &snode, &select)) {
if (!ed_node_link_conditions(area, &snode, &select)) {
return;
}
@@ -2428,3 +2429,166 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Node Dissolve Links
* \{ */
namespace blender {
typedef MultiValueMap<bNodeSocket *, bNodeLink *> NodeEditDissolveLinkMap;
NodeEditDissolveLinkMap node_dissolve_build_link_map(bNodeTree *ntree)
{
NodeEditDissolveLinkMap socket_links;
/* Map all links to a selected node. */
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (link->tonode->flag & NODE_SELECT) {
socket_links.add(link->tosock, link);
}
}
/* Map internal links that connect node inputs to outputs. */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & NODE_SELECT) {
LISTBASE_FOREACH (bNodeLink *, link, &node->internal_links) {
socket_links.add(link->tosock, link);
}
}
}
return socket_links;
}
Vector<bNodeLink *> node_dissolve_find_incoming_links(const NodeEditDissolveLinkMap &socket_links,
bNodeLink *out_link)
{
Stack<bNodeLink *> link_stack;
Set<bNodeSocket *> visited;
Vector<bNodeLink *> result;
/* Depth-first search to find links that have no fromsock in the map. */
link_stack.push(out_link);
do {
bNodeLink *link = link_stack.pop();
if (visited.contains(link->fromsock)) {
continue;
}
visited.add(link->fromsock);
const Span<bNodeLink *> in_links = socket_links.lookup(link->fromsock);
if (in_links.is_empty()) {
/* Place incoming links from unselected nodes in the result. */
if (!(link->fromnode->flag & NODE_SELECT)) {
/* Should only have actual tree links from unselected nodes, never internal links. */
BLI_assert(link->fromsock->in_out == SOCK_OUT && link->tosock->in_out == SOCK_IN);
result.append(link);
}
}
else {
link_stack.push_multiple(in_links);
}
} while (!link_stack.is_empty());
return result;
}
} // namespace blender
/* Replace links to and from selected nodes.
* Appropriate direct connections are created to replace connections if possible.
* Data about replaced links is returned and can be used to undo the dissolve.
* Note: Dissolve data becomes invalid if nodes or links are removed from the tree.
*/
void ED_node_dissolve_links(Main *bmain, bNodeTree *ntree, NodeEditDissolveLinksData *data)
{
/* Links that are being removed, for output data. */
blender::Vector<bNodeLink> removed_links;
/* Replacement links that have been added, for output data. */
blender::Vector<bNodeLink *> added_links;
/* Incoming links for sockets.
* Most node systems only allow one incoming socket link,
* but the map allows for more than one link if needed. */
blender::NodeEditDissolveLinkMap socket_links = blender::node_dissolve_build_link_map(ntree);
/* Find outgoing links from the selected nodes and replace them. */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
const bool from_selected = link->fromnode->flag & NODE_SELECT;
const bool to_selected = link->tonode->flag & NODE_SELECT;
if (from_selected && !to_selected) {
/* Find all incoming links connected to the outgoing link. */
blender::Vector<bNodeLink *> in_links = blender::node_dissolve_find_incoming_links(
socket_links, link);
for (bNodeLink *in_link : in_links) {
bNodeLink *replace_link = nodeAddLink(
ntree, in_link->fromnode, in_link->fromsock, link->tonode, link->tosock);
/* Remember for output. */
if (data) {
added_links.append(replace_link);
}
}
/* Remember for output. */
if (data) {
removed_links.append(*link);
}
/* This link has been replaced. */
nodeRemLink(ntree, link);
}
}
/* Find incoming links from the selected nodes and remove them.
* Note: this must happen after all outgoing links have been replaced.
*/
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
const bool from_selected = link->fromnode->flag & NODE_SELECT;
const bool to_selected = link->tonode->flag & NODE_SELECT;
if (!from_selected && to_selected) {
/* Remember for output. */
if (data) {
removed_links.append(*link);
}
/* This link has been replaced. */
nodeRemLink(ntree, link);
}
}
if (data) {
data->tot_removed_links = removed_links.size();
data->tot_added_links = added_links.size();
data->removed_links = (bNodeLink *)MEM_mallocN(sizeof(bNodeLink) * removed_links.size(),
"removed links");
memcpy(data->removed_links, removed_links.data(), sizeof(bNodeLink) * removed_links.size());
data->added_links = (bNodeLink **)MEM_mallocN(sizeof(bNodeLink *) * added_links.size(),
"removed links");
memcpy(data->added_links, added_links.data(), sizeof(bNodeLink *) * added_links.size());
}
ntreeUpdateTree(bmain, ntree);
}
/* Restore links stored in the dissolve data. */
void ED_node_dissolve_undo(Main *bmain, bNodeTree *ntree, const NodeEditDissolveLinksData *data)
{
for (bNodeLink *link : blender::Span<bNodeLink *>(data->added_links, data->tot_added_links)) {
nodeRemLink(ntree, link);
}
for (const bNodeLink &link :
blender::Span<bNodeLink>(data->removed_links, data->tot_removed_links)) {
nodeAddLink(ntree, link.fromnode, link.fromsock, link.tonode, link.tosock);
}
ntreeUpdateTree(bmain, ntree);
}
/* Free node dissolve data. */
void ED_node_dissolve_free_data(NodeEditDissolveLinksData *data)
{
if (data) {
MEM_freeN(data->removed_links);
MEM_freeN(data->added_links);
MEM_freeN(data);
}
}
/** \} */

View File

@@ -625,7 +625,9 @@ static bool transform_modal_item_poll(const wmOperator *op, int value)
}
break;
}
case TFM_MODAL_INSERTOFS_TOGGLE_DIR: {
case TFM_MODAL_INSERTOFS_TOGGLE_DIR:
case TFM_MODAL_NODE_UNLINK_ON:
case TFM_MODAL_NODE_UNLINK_OFF: {
if (t->spacetype != SPACE_NODE) {
return false;
}
@@ -696,6 +698,8 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
{TFM_MODAL_AUTOCONSTRAINT, "AUTOCONSTRAIN", 0, "Automatic Constraint", ""},
{TFM_MODAL_AUTOCONSTRAINTPLANE, "AUTOCONSTRAINPLANE", 0, "Automatic Constraint Plane", ""},
{TFM_MODAL_PRECISION, "PRECISION", 0, "Precision Mode", ""},
{TFM_MODAL_NODE_UNLINK_ON, "NODE_UNLINK_ON", 0, "Unlink nodes", ""},
{TFM_MODAL_NODE_UNLINK_OFF, "NODE_UNLINK_OFF", 0, "Unlink nodes (Off)", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -1065,6 +1069,16 @@ int transformEvent(TransInfo *t, const wmEvent *event)
t->redraw |= TREDRAW_SOFT;
}
break;
case TFM_MODAL_NODE_UNLINK_ON:
t->flag |= T_NODE_UNLINK;
t->redraw |= TREDRAW_HARD;
handled = true;
break;
case TFM_MODAL_NODE_UNLINK_OFF:
t->flag &= ~T_NODE_UNLINK;
t->redraw |= TREDRAW_HARD;
handled = true;
break;
case TFM_MODAL_AUTOCONSTRAINT:
case TFM_MODAL_AUTOCONSTRAINTPLANE:
if ((t->flag & T_RELEASE_CONFIRM) && (event->prevval == KM_RELEASE) &&

View File

@@ -152,6 +152,9 @@ typedef enum {
/** No cursor wrapping on region bounds */
T_NO_CURSOR_WRAP = 1 << 23,
/** Nodes detach from existing links and don't insert on intersected links. */
T_NODE_UNLINK = 1 << 24,
} eTFlag;
/** #TransInfo.modifiers */
@@ -289,6 +292,10 @@ enum {
TFM_MODAL_AUTOCONSTRAINTPLANE = 29,
TFM_MODAL_PRECISION = 30,
/** Node link intersect toggle. */
TFM_MODAL_NODE_UNLINK_ON = 31,
TFM_MODAL_NODE_UNLINK_OFF = 32,
};
/** \} */

View File

@@ -50,6 +50,7 @@ typedef struct NodeTransCustomData {
/* Initial rect of the view2d, used for computing offset during edge panning */
rctf initial_v2d_cur;
View2DEdgePanData edge_pan;
NodeEditDissolveLinksData *dissolve_links_data;
} NodeTransCustomData;
/* transcribe given node into TransData2D for Transforming */
@@ -174,9 +175,9 @@ void createTransNodeData(TransInfo *t)
void flushTransNodes(TransInfo *t)
{
const float dpi_fac = UI_DPI_FAC;
struct Main *bmain = CTX_data_main(t->context);
NodeTransCustomData *customdata = (NodeTransCustomData *)t->custom.type.data;
const float dpi_fac = UI_DPI_FAC;
if (t->mode == TFM_TRANSLATION) {
/* Edge panning functions expect window coordinates, mval is relative to region */
@@ -222,9 +223,22 @@ void flushTransNodes(TransInfo *t)
node->locy = loc[1];
}
/* handle intersection with noodles */
if (tc->data_len == 1) {
ED_node_link_intersect_test(t->area, 1);
/* Handle intersection with links. */
if (t->flag & T_NODE_UNLINK) {
ED_node_link_hilite_clear(t->area);
if (customdata->dissolve_links_data == NULL) {
/* Unlink transformed nodes. */
SpaceNode *snode = t->area->spacedata.first;
bNodeTree *ntree = snode->edittree;
customdata->dissolve_links_data = MEM_callocN(sizeof(NodeEditDissolveLinksData),
"node link dissolve data");
ED_node_dissolve_links(bmain, ntree, customdata->dissolve_links_data);
}
}
else if (tc->data_len == 1) {
ED_node_link_hilite_intersected(t->area);
}
}
}
@@ -238,29 +252,38 @@ void flushTransNodes(TransInfo *t)
void special_aftertrans_update__node(bContext *C, TransInfo *t)
{
struct Main *bmain = CTX_data_main(C);
NodeTransCustomData *customdata = (NodeTransCustomData *)t->custom.type.data;
const bool canceled = (t->state == TRANS_CANCEL);
SpaceNode *snode = (SpaceNode *)t->area->spacedata.first;
if (canceled && t->remove_on_cancel) {
/* remove selected nodes on cancel */
bNodeTree *ntree = snode->edittree;
if (ntree) {
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
if (node->flag & NODE_SELECT) {
nodeRemoveNode(bmain, ntree, node, true);
bNodeTree *ntree = snode->edittree;
if (canceled) {
if (t->remove_on_cancel) {
/* remove selected nodes on cancel */
if (ntree) {
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
if (node->flag & NODE_SELECT) {
nodeRemoveNode(bmain, ntree, node, true);
}
}
ntreeUpdateTree(bmain, ntree);
}
ntreeUpdateTree(bmain, ntree);
}
else if (customdata->dissolve_links_data) {
ED_node_dissolve_undo(bmain, ntree, customdata->dissolve_links_data);
}
}
else {
ED_node_post_apply_transform(C, snode->edittree);
if (!(t->flag & T_NODE_UNLINK)) {
ED_node_link_hilite_insert(bmain, t->area);
}
}
if (!canceled) {
ED_node_post_apply_transform(C, snode->edittree);
ED_node_link_insert(bmain, t->area);
}
/* clear link line */
ED_node_link_intersect_test(t->area, 0);
ED_node_dissolve_free_data(customdata->dissolve_links_data);
ED_node_link_hilite_clear(t->area);
}
/** \} */

View File

@@ -634,6 +634,15 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
t->flag |= T_OVERRIDE_CENTER;
}
if (t->spacetype == SPACE_NODE) {
if (op && (prop = RNA_struct_find_property(op->ptr, "node_unlink")) &&
RNA_property_is_set(op->ptr, prop)) {
if (RNA_property_boolean_get(op->ptr, prop)) {
t->flag |= T_NODE_UNLINK;
}
}
}
setTransformViewMatrices(t);
initNumInput(&t->num);
}

View File

@@ -692,6 +692,14 @@ void Transform_Properties(struct wmOperatorType *ot, int flags)
ot->srna, "correct_uv", true, "Correct UVs", "Correct UV coordinates when transforming");
}
if (flags & P_NODES) {
RNA_def_boolean(ot->srna,
"node_unlink",
false,
"Unlink Nodes",
"Detach nodes from links and don't insert on intersected links");
}
if (flags & P_CENTER) {
/* For gizmos that define their own center. */
prop = RNA_def_property(ot->srna, "center_override", PROP_FLOAT, PROP_XYZ);
@@ -745,7 +753,8 @@ static void TRANSFORM_OT_translate(struct wmOperatorType *ot)
Transform_Properties(ot,
P_ORIENT_MATRIX | P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_ALIGN_SNAP |
P_OPTIONS | P_GPENCIL_EDIT | P_CURSOR_EDIT | P_POST_TRANSFORM);
P_OPTIONS | P_GPENCIL_EDIT | P_CURSOR_EDIT | P_NODES |
P_POST_TRANSFORM);
}
static void TRANSFORM_OT_resize(struct wmOperatorType *ot)