Node Editor: Paste nodes on mouse position
When pasting nodes with the shortcut or the context menu, place the center of the selected nodes at the same position as the mouse cursor. This should save time, and is more intuitive because the new nodes are actually visible. Based on a patch by Juanfran Matheu (@jfmatheu). Differential Revision: https://developer.blender.org/D10787
This commit is contained in:
parent
e091291b5b
commit
7355d64f2b
@ -318,7 +318,9 @@ class NODE_MT_node(Menu):
|
|||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.operator("node.clipboard_copy", text="Copy")
|
layout.operator("node.clipboard_copy", text="Copy")
|
||||||
layout.operator("node.clipboard_paste", text="Paste")
|
row = layout.row()
|
||||||
|
row.operator_context = 'EXEC_DEFAULT'
|
||||||
|
row.operator("node.clipboard_paste", text="Paste")
|
||||||
layout.operator("node.duplicate_move")
|
layout.operator("node.duplicate_move")
|
||||||
layout.operator("node.duplicate_move_linked")
|
layout.operator("node.duplicate_move_linked")
|
||||||
layout.operator("node.delete")
|
layout.operator("node.delete")
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
#include "ED_render.h"
|
#include "ED_render.h"
|
||||||
#include "ED_screen.h"
|
#include "ED_screen.h"
|
||||||
|
|
||||||
|
#include "RNA_access.h"
|
||||||
|
#include "RNA_define.h"
|
||||||
|
|
||||||
#include "DEG_depsgraph_build.h"
|
#include "DEG_depsgraph_build.h"
|
||||||
|
|
||||||
#include "node_intern.hh"
|
#include "node_intern.hh"
|
||||||
@ -24,6 +27,11 @@ namespace blender::ed::space_node {
|
|||||||
|
|
||||||
struct NodeClipboardItem {
|
struct NodeClipboardItem {
|
||||||
bNode *node;
|
bNode *node;
|
||||||
|
/**
|
||||||
|
* The offset and size of the node from when it was drawn. Stored here since it doesn't remain
|
||||||
|
* valid for the nodes in the clipboard.
|
||||||
|
*/
|
||||||
|
rctf draw_rect;
|
||||||
|
|
||||||
/* Extra info to validate the node on creation. Otherwise we may reference missing data. */
|
/* Extra info to validate the node on creation. Otherwise we may reference missing data. */
|
||||||
ID *id;
|
ID *id;
|
||||||
@ -74,15 +82,24 @@ struct NodeClipboard {
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_node(bNode *node)
|
void add_node(const bNode &node,
|
||||||
|
Map<const bNode *, bNode *> &node_map,
|
||||||
|
Map<const bNodeSocket *, bNodeSocket *> &socket_map)
|
||||||
{
|
{
|
||||||
|
/* No ID refcounting, this node is virtual,
|
||||||
|
* detached from any actual Blender data currently. */
|
||||||
|
bNode *new_node = bke::node_copy_with_mapping(
|
||||||
|
nullptr, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN, false, socket_map);
|
||||||
|
node_map.add_new(&node, new_node);
|
||||||
|
|
||||||
NodeClipboardItem item;
|
NodeClipboardItem item;
|
||||||
item.node = node;
|
item.draw_rect = node.runtime->totr;
|
||||||
item.id = node->id;
|
item.node = new_node;
|
||||||
|
item.id = new_node->id;
|
||||||
if (item.id) {
|
if (item.id) {
|
||||||
item.id_name = node->id->name;
|
item.id_name = new_node->id->name;
|
||||||
if (ID_IS_LINKED(node->id)) {
|
if (ID_IS_LINKED(new_node->id)) {
|
||||||
item.library_name = node->id->lib->filepath_abs;
|
item.library_name = new_node->id->lib->filepath_abs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->nodes.append(std::move(item));
|
this->nodes.append(std::move(item));
|
||||||
@ -110,22 +127,13 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator * /*op*/)
|
|||||||
Map<const bNode *, bNode *> node_map;
|
Map<const bNode *, bNode *> node_map;
|
||||||
Map<const bNodeSocket *, bNodeSocket *> socket_map;
|
Map<const bNodeSocket *, bNodeSocket *> socket_map;
|
||||||
|
|
||||||
for (bNode *node : tree.all_nodes()) {
|
for (const bNode *node : tree.all_nodes()) {
|
||||||
if (node->flag & SELECT) {
|
if (node->flag & SELECT) {
|
||||||
/* No ID reference counting, this node is virtual, detached from any actual Blender data. */
|
clipboard.add_node(*node, node_map, socket_map);
|
||||||
bNode *new_node = bke::node_copy_with_mapping(nullptr,
|
|
||||||
*node,
|
|
||||||
LIB_ID_CREATE_NO_USER_REFCOUNT |
|
|
||||||
LIB_ID_CREATE_NO_MAIN,
|
|
||||||
false,
|
|
||||||
socket_map);
|
|
||||||
node_map.add_new(node, new_node);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (bNode *new_node : node_map.values()) {
|
for (bNode *new_node : node_map.values()) {
|
||||||
clipboard.add_node(new_node);
|
|
||||||
|
|
||||||
/* Parent pointer must be redirected to new node or detached if parent is not copied. */
|
/* Parent pointer must be redirected to new node or detached if parent is not copied. */
|
||||||
if (new_node->parent) {
|
if (new_node->parent) {
|
||||||
if (node_map.contains(new_node->parent)) {
|
if (node_map.contains(new_node->parent)) {
|
||||||
@ -197,14 +205,6 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
|
|||||||
|
|
||||||
node_deselect_all(tree);
|
node_deselect_all(tree);
|
||||||
|
|
||||||
/* calculate "barycenter" for placing on mouse cursor */
|
|
||||||
float2 center = {0.0f, 0.0f};
|
|
||||||
for (const NodeClipboardItem &item : clipboard.nodes) {
|
|
||||||
center.x += BLI_rctf_cent_x(&item.node->runtime->totr);
|
|
||||||
center.y += BLI_rctf_cent_y(&item.node->runtime->totr);
|
|
||||||
}
|
|
||||||
center /= clipboard.nodes.size();
|
|
||||||
|
|
||||||
Map<const bNode *, bNode *> node_map;
|
Map<const bNode *, bNode *> node_map;
|
||||||
Map<const bNodeSocket *, bNodeSocket *> socket_map;
|
Map<const bNodeSocket *, bNodeSocket *> socket_map;
|
||||||
|
|
||||||
@ -248,6 +248,27 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PropertyRNA *offset_prop = RNA_struct_find_property(op->ptr, "offset");
|
||||||
|
if (RNA_property_is_set(op->ptr, offset_prop)) {
|
||||||
|
float2 center(0);
|
||||||
|
for (NodeClipboardItem &item : clipboard.nodes) {
|
||||||
|
center.x += BLI_rctf_cent_x(&item.draw_rect);
|
||||||
|
center.y += BLI_rctf_cent_y(&item.draw_rect);
|
||||||
|
}
|
||||||
|
/* DPI factor needs to be removed when computing a View2D offset from drawing rects. */
|
||||||
|
center /= clipboard.nodes.size();
|
||||||
|
center /= UI_DPI_FAC;
|
||||||
|
|
||||||
|
float2 mouse_location;
|
||||||
|
RNA_property_float_get_array(op->ptr, offset_prop, mouse_location);
|
||||||
|
const float2 offset = mouse_location - center;
|
||||||
|
|
||||||
|
for (bNode *new_node : node_map.values()) {
|
||||||
|
new_node->locx += offset.x;
|
||||||
|
new_node->locy += offset.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Add links between existing nodes. */
|
/* Add links between existing nodes. */
|
||||||
for (const bNodeLink &link : clipboard.links) {
|
for (const bNodeLink &link : clipboard.links) {
|
||||||
const bNode *fromnode = link.fromnode;
|
const bNode *fromnode = link.fromnode;
|
||||||
@ -276,16 +297,40 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
|
|||||||
return OPERATOR_FINISHED;
|
return OPERATOR_FINISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int node_clipboard_paste_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||||
|
{
|
||||||
|
const ARegion *region = CTX_wm_region(C);
|
||||||
|
float2 cursor;
|
||||||
|
UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor.x, &cursor.y);
|
||||||
|
RNA_float_set_array(op->ptr, "offset", cursor);
|
||||||
|
return node_clipboard_paste_exec(C, op);
|
||||||
|
}
|
||||||
|
|
||||||
void NODE_OT_clipboard_paste(wmOperatorType *ot)
|
void NODE_OT_clipboard_paste(wmOperatorType *ot)
|
||||||
{
|
{
|
||||||
ot->name = "Paste from Clipboard";
|
ot->name = "Paste from Clipboard";
|
||||||
ot->description = "Pastes nodes from the clipboard to the active node tree";
|
ot->description = "Pastes nodes from the clipboard to the active node tree";
|
||||||
ot->idname = "NODE_OT_clipboard_paste";
|
ot->idname = "NODE_OT_clipboard_paste";
|
||||||
|
|
||||||
|
ot->invoke = node_clipboard_paste_invoke;
|
||||||
ot->exec = node_clipboard_paste_exec;
|
ot->exec = node_clipboard_paste_exec;
|
||||||
ot->poll = ED_operator_node_editable;
|
ot->poll = ED_operator_node_editable;
|
||||||
|
|
||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
|
||||||
|
PropertyRNA *prop = RNA_def_float_array(
|
||||||
|
ot->srna,
|
||||||
|
"offset",
|
||||||
|
2,
|
||||||
|
nullptr,
|
||||||
|
-FLT_MAX,
|
||||||
|
FLT_MAX,
|
||||||
|
"Location",
|
||||||
|
"The 2D view location for the center of the new nodes, or unchanged if not set",
|
||||||
|
-FLT_MAX,
|
||||||
|
FLT_MAX);
|
||||||
|
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||||
|
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
Loading…
Reference in New Issue
Block a user