WIP: Node Editor: Improve working with frame nodes #108358
|
@ -6138,8 +6138,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_without_navigaton}, None),
|
||||
("AUTOCONSTRAINPLANE", {"type": 'MIDDLEMOUSE', "value": 'ANY', "shift": True, **alt_without_navigaton}, None),
|
||||
("PRECISION", {"type": 'LEFT_SHIFT', "value": 'ANY', "any": True}, None),
|
||||
|
|
|
@ -4270,8 +4270,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),
|
||||
|
|
|
@ -31,6 +31,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
|
||||
|
|
|
@ -227,12 +227,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;
|
||||
|
||||
|
|
|
@ -270,6 +270,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<NodeFrame *>(a->storage)->flag & NODE_FRAME_RESIZING) {
|
||||
return true;
|
||||
}
|
||||
if (static_cast<NodeFrame *>(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;
|
||||
|
@ -3303,31 +3321,34 @@ static void frame_node_prepare_for_draw(bNode &node, Span<bNode *> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -911,10 +911,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;
|
||||
|
@ -925,6 +926,15 @@ static void node_resize_exit(bContext *C, wmOperator *op, bool cancel)
|
|||
node->height = nsw->oldheight;
|
||||
}
|
||||
|
||||
if (node->is_frame()) {
|
||||
NodeFrame *data = static_cast<NodeFrame *>(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;
|
||||
}
|
||||
|
@ -1053,6 +1063,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<NodeFrame *>(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;
|
||||
}
|
||||
|
|
|
@ -309,7 +309,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);
|
||||
|
|
|
@ -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);
|
||||
|
@ -143,7 +142,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",
|
||||
|
@ -153,7 +151,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.
|
||||
|
@ -165,7 +162,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",
|
||||
|
|
|
@ -1897,83 +1897,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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -2424,8 +2407,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;
|
||||
|
@ -2448,43 +2430,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)))
|
||||
{
|
||||
rctf totr_frame;
|
||||
|
||||
/* check nodes front to back */
|
||||
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)) {
|
||||
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 :
|
||||
|
@ -2616,7 +2561,7 @@ static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent
|
|||
iofsd->anim_timer = WM_event_timer_add(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);
|
||||
|
|
|
@ -619,8 +619,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;
|
||||
}
|
||||
|
@ -746,8 +748,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", ""},
|
||||
|
@ -1209,13 +1221,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;
|
||||
|
@ -2006,13 +2028,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_OFF && kmi->val == KM_PRESS) {
|
||||
if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) &&
|
||||
(event->modifier & KM_CTRL)) ||
|
||||
(ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) &&
|
||||
|
@ -2020,7 +2042,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;
|
||||
}
|
||||
|
|
|
@ -163,11 +163,12 @@ enum eTModifier {
|
|||
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,
|
||||
};
|
||||
ENUM_OPERATORS(eTModifier, MOD_NODE_ATTACH)
|
||||
ENUM_OPERATORS(eTModifier, MOD_EDIT_SNAP_SOURCE)
|
||||
|
||||
/** #TransSnap.status */
|
||||
enum eTSnap {
|
||||
|
@ -264,8 +265,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,
|
||||
|
@ -283,6 +284,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,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -32,23 +32,26 @@
|
|||
|
||||
#include "WM_api.hh"
|
||||
|
||||
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)
|
||||
{
|
||||
/* account for parents (nested nodes) */
|
||||
const blender::float2 node_offset = {node.offsetx, node.offsety};
|
||||
blender::float2 loc = blender::bke::nodeToView(&node, blender::math::round(node_offset));
|
||||
loc *= dpi_fac;
|
||||
|
@ -80,6 +83,8 @@ static void create_transform_data_for_node(TransData &td,
|
|||
unit_m3(td.smtx);
|
||||
|
||||
td.extra = &node;
|
||||
|
||||
tdc.parent = node.parent;
|
||||
}
|
||||
|
||||
static bool is_node_parent_select(const bNode *node)
|
||||
|
@ -92,6 +97,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<bNode *>(td->extra);
|
||||
|
||||
TransCustomDataNode *tdc = static_cast<TransCustomDataNode *>(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;
|
||||
|
@ -103,7 +142,7 @@ static void createTransNodeData(bContext * /*C*/, TransInfo *t)
|
|||
}
|
||||
|
||||
/* Custom data to enable edge panning during the node transform */
|
||||
TransCustomDataNode *customdata = MEM_cnew<TransCustomDataNode>(__func__);
|
||||
TransInfoCustomDataNode *customdata = MEM_cnew<TransInfoCustomDataNode>(__func__);
|
||||
UI_view2d_edge_pan_init(t->context,
|
||||
&customdata->edgepan_data,
|
||||
NODE_EDGE_PAN_INSIDE_PAD,
|
||||
|
@ -114,11 +153,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;
|
||||
|
@ -138,9 +181,13 @@ static void createTransNodeData(bContext * /*C*/, TransInfo *t)
|
|||
tc->data_len = nodes.size();
|
||||
tc->data = MEM_cnew_array<TransData>(tc->data_len, __func__);
|
||||
tc->data_2d = MEM_cnew_array<TransData2D>(tc->data_len, __func__);
|
||||
tc->custom.type.data = MEM_cnew_array<TransCustomDataNode>(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<TransCustomDataNode *>(tc->custom.type.data);
|
||||
create_transform_data_for_node(
|
||||
tc->data[i], tc->data_2d[i], data_node[i], *nodes[i], UI_SCALE_FAC);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,8 +244,9 @@ static void flushTransNodes(TransInfo *t)
|
|||
using namespace blender::ed;
|
||||
const float dpi_fac = UI_SCALE_FAC;
|
||||
SpaceNode *snode = static_cast<SpaceNode *>(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) {
|
||||
|
@ -247,13 +295,29 @@ static void flushTransNodes(TransInfo *t)
|
|||
node->locy = location.y;
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -288,12 +352,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);
|
||||
|
|
|
@ -1895,12 +1895,11 @@ enum {
|
|||
};
|
||||
|
||||
/* Frame node flags. */
|
||||
|
||||
enum {
|
||||
/** Keep the bounding box minimal. */
|
||||
NODE_FRAME_SHRINK = 1,
|
||||
/** Test flag, if frame can be resized by user. */
|
||||
NODE_FRAME_RESIZEABLE = 2,
|
||||
/** Temporary flag for when the frame is resized by user. */
|
||||
NODE_FRAME_RESIZING = 2,
|
||||
};
|
||||
|
||||
/* Proxy node flags. */
|
||||
|
|
Loading…
Reference in New Issue