|
|
|
@ -2587,4 +2587,220 @@ void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot)
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Slide Nodes Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
struct NodeSlideData {
|
|
|
|
|
int2 initial_mouse_pos;
|
|
|
|
|
Vector<float2> initial_node_positions;
|
|
|
|
|
Vector<bNode *> nodes_to_slide_left;
|
|
|
|
|
Vector<bNode *> nodes_to_slide_right;
|
|
|
|
|
|
|
|
|
|
/* TODO: No need to store the two vectors above. */
|
|
|
|
|
Vector<bNode *> nodes_to_slide;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static Vector<bNode *> find_nodes_to_the_left(const Span<bNode *> trigger_nodes)
|
|
|
|
|
{
|
|
|
|
|
VectorSet<bNode *> found_nodes;
|
|
|
|
|
Stack<bNode *> nodes_to_check = trigger_nodes;
|
|
|
|
|
while (!nodes_to_check.is_empty()) {
|
|
|
|
|
bNode &node = *nodes_to_check.pop();
|
|
|
|
|
if (!found_nodes.add(&node)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (bNodeSocket *socket : node.input_sockets()) {
|
|
|
|
|
if (!socket->is_visible()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (bNodeSocket *from_socket : socket->directly_linked_sockets()) {
|
|
|
|
|
nodes_to_check.push(&from_socket->owner_node());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return found_nodes.as_span();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Vector<bNode *> find_nodes_to_the_right(const Span<bNode *> trigger_nodes)
|
|
|
|
|
{
|
|
|
|
|
VectorSet<bNode *> found_nodes;
|
|
|
|
|
Stack<bNode *> nodes_to_check = trigger_nodes;
|
|
|
|
|
while (!nodes_to_check.is_empty()) {
|
|
|
|
|
bNode &node = *nodes_to_check.pop();
|
|
|
|
|
if (!found_nodes.add(&node)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (bNodeSocket *socket : node.output_sockets()) {
|
|
|
|
|
if (!socket->is_visible()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (bNodeSocket *from_socket : socket->directly_linked_sockets()) {
|
|
|
|
|
nodes_to_check.push(&from_socket->owner_node());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return found_nodes.as_span();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Vector<bNode *> get_nodes_in_frame(bNode &frame)
|
|
|
|
|
{
|
|
|
|
|
Vector<bNode *> nodes;
|
|
|
|
|
Stack<bNode *> frames_to_check = {&frame};
|
|
|
|
|
while (!frames_to_check.is_empty()) {
|
|
|
|
|
bNode &frame = *frames_to_check.pop();
|
|
|
|
|
for (bNode *node : frame.direct_children_in_frame()) {
|
|
|
|
|
if (node->is_frame()) {
|
|
|
|
|
frames_to_check.push(node);
|
|
|
|
|
}
|
|
|
|
|
nodes.append(node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nodes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int node_slide_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
|
{
|
|
|
|
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
|
|
|
|
bNodeTree &tree = *snode.edittree;
|
|
|
|
|
ARegion ®ion = *CTX_wm_region(C);
|
|
|
|
|
View2D &v2d = region.v2d;
|
|
|
|
|
tree.ensure_topology_cache();
|
|
|
|
|
|
|
|
|
|
NodeSlideData *slide_data = MEM_new<NodeSlideData>(__func__);
|
|
|
|
|
op->customdata = slide_data;
|
|
|
|
|
|
|
|
|
|
slide_data->initial_mouse_pos = event->mval;
|
|
|
|
|
|
|
|
|
|
Vector<bNode *> trigger_nodes;
|
|
|
|
|
for (bNode *node : tree.all_nodes()) {
|
|
|
|
|
slide_data->initial_node_positions.append(float2(node->locx, node->locy));
|
|
|
|
|
if (node->flag & NODE_SELECT) {
|
|
|
|
|
trigger_nodes.append(node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (trigger_nodes.is_empty()) {
|
|
|
|
|
float x, y;
|
|
|
|
|
UI_view2d_region_to_view(&v2d, event->mval[0], event->mval[1], &x, &y);
|
|
|
|
|
|
|
|
|
|
bNode *smallest_hovered_frame = nullptr;
|
|
|
|
|
float smallest_frame_width = FLT_MAX;
|
|
|
|
|
for (bNode *node : tree.all_nodes()) {
|
|
|
|
|
if (!node->is_frame()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!BLI_rctf_isect_pt(&node->runtime->totr, x, y)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const float width = BLI_rctf_size_x(&node->runtime->totr);
|
|
|
|
|
if (smallest_hovered_frame != nullptr) {
|
|
|
|
|
if (width > smallest_frame_width) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
smallest_hovered_frame = node;
|
|
|
|
|
smallest_frame_width = width;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector<bNode *> trigger_candidates = smallest_hovered_frame ?
|
|
|
|
|
get_nodes_in_frame(*smallest_hovered_frame) :
|
|
|
|
|
Vector<bNode *>(tree.all_nodes());
|
|
|
|
|
|
|
|
|
|
Vector<bNode *> nodes_left;
|
|
|
|
|
Vector<bNode *> nodes_right;
|
|
|
|
|
for (bNode *node : trigger_candidates) {
|
|
|
|
|
if (node->runtime->totr.xmin < x) {
|
|
|
|
|
nodes_left.append(node);
|
|
|
|
|
}
|
|
|
|
|
if (node->runtime->totr.xmax > x) {
|
|
|
|
|
nodes_right.append(node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
slide_data->nodes_to_slide_left = std::move(nodes_left);
|
|
|
|
|
slide_data->nodes_to_slide_right = std::move(nodes_right);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
slide_data->nodes_to_slide_left = find_nodes_to_the_left(trigger_nodes);
|
|
|
|
|
slide_data->nodes_to_slide_right = find_nodes_to_the_right(trigger_nodes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WM_event_add_modal_handler(C, op);
|
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int node_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
|
{
|
|
|
|
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
|
|
|
|
bNodeTree &tree = *snode.edittree;
|
|
|
|
|
ARegion ®ion = *CTX_wm_region(C);
|
|
|
|
|
View2D &v2d = region.v2d;
|
|
|
|
|
|
|
|
|
|
NodeSlideData &slide_data = *static_cast<NodeSlideData *>(op->customdata);
|
|
|
|
|
|
|
|
|
|
float2 initial_mouse;
|
|
|
|
|
float2 current_mouse;
|
|
|
|
|
UI_view2d_region_to_view(&v2d,
|
|
|
|
|
slide_data.initial_mouse_pos.x,
|
|
|
|
|
slide_data.initial_mouse_pos.y,
|
|
|
|
|
&initial_mouse.x,
|
|
|
|
|
&initial_mouse.y);
|
|
|
|
|
UI_view2d_region_to_view(
|
|
|
|
|
&v2d, event->mval[0], event->mval[1], ¤t_mouse.x, ¤t_mouse.y);
|
|
|
|
|
|
|
|
|
|
switch (event->type) {
|
|
|
|
|
case MOUSEMOVE: {
|
|
|
|
|
const float node_diff_x = (current_mouse.x - initial_mouse.x) * UI_INV_SCALE_FAC;
|
|
|
|
|
if (slide_data.nodes_to_slide.is_empty()) {
|
|
|
|
|
const float move_threshold = 3.0f;
|
|
|
|
|
if (node_diff_x < -move_threshold) {
|
|
|
|
|
slide_data.nodes_to_slide = slide_data.nodes_to_slide_left;
|
|
|
|
|
}
|
|
|
|
|
if (node_diff_x > move_threshold) {
|
|
|
|
|
slide_data.nodes_to_slide = slide_data.nodes_to_slide_right;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (bNode *node : slide_data.nodes_to_slide) {
|
|
|
|
|
if (node->type == NODE_FRAME) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
node->locx = slide_data.initial_node_positions[node->index()].x + node_diff_x;
|
|
|
|
|
}
|
|
|
|
|
ED_region_tag_redraw(®ion);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case RIGHTMOUSE:
|
|
|
|
|
case EVT_ESCKEY: {
|
|
|
|
|
for (bNode *node : tree.all_nodes()) {
|
|
|
|
|
node->locx = slide_data.initial_node_positions[node->index()].x;
|
|
|
|
|
}
|
|
|
|
|
ED_region_tag_redraw(®ion);
|
|
|
|
|
MEM_delete(&slide_data);
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
case LEFTMOUSE: {
|
|
|
|
|
MEM_delete(&slide_data);
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_slide(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Slide Nodes";
|
|
|
|
|
ot->description = "Make space for new nodes at the mouse position";
|
|
|
|
|
ot->idname = "NODE_OT_slide";
|
|
|
|
|
|
|
|
|
|
ot->invoke = node_slide_invoke;
|
|
|
|
|
ot->modal = node_slide_modal;
|
|
|
|
|
ot->poll = ED_operator_node_editable;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
} // namespace blender::ed::space_node
|
|
|
|
|