void NodeGraph::setNodeToDefaultPosition(const NodeGuiPtr& node, const NodesGuiList& selectedNodes, const CreateNodeArgs& args) { NodePtr internalNode = node->getNode(); // Serializatino, don't do anything SERIALIZATION_NAMESPACE::NodeSerializationPtr serialization = args.getPropertyUnsafe<SERIALIZATION_NAMESPACE::NodeSerializationPtr >(kCreateNodeArgsPropNodeSerialization); if (serialization) { return; } bool hasPositionnedNode = false; // Try to autoconnect if there is a selection bool autoConnect = args.getPropertyUnsafe<bool>(kCreateNodeArgsPropAutoConnect); if ( selectedNodes.empty() || serialization) { autoConnect = false; } if (autoConnect) { BackdropGuiPtr isBd = toBackdropGui(node); if (!isBd) { NodeGuiPtr selectedNode; if ( !serialization && (selectedNodes.size() == 1) ) { selectedNode = selectedNodes.front(); BackdropGuiPtr isBdGui = toBackdropGui(selectedNode); if (isBdGui) { selectedNode.reset(); } } if (selectedNode && node->getNode()->autoConnect(selectedNode->getNode())) { hasPositionnedNode = true; } } } if (!hasPositionnedNode) { // If there's a position hint, use it to position the node double xPosHint = args.getPropertyUnsafe<double>(kCreateNodeArgsPropNodeInitialPosition, 0); double yPosHint = args.getPropertyUnsafe<double>(kCreateNodeArgsPropNodeInitialPosition, 1); if ((xPosHint != INT_MIN) && (yPosHint != INT_MIN)) { QPointF pos = node->mapToParent( node->mapFromScene( QPointF(xPosHint, yPosHint) ) ); node->refreshPosition( pos.x(), pos.y(), true ); hasPositionnedNode = true; } } // Ok fallback with the node in the middle of the node graph if (!hasPositionnedNode) { moveNodeToCenterOfVisiblePortion(node); } }
NATRON_NAMESPACE_ENTER //using std::cout; using std::endl; void NodeGraph::moveNodeToCenterOfVisiblePortion(const NodeGuiPtr &n) { QRectF viewPos = visibleSceneRect(); QPointF position; position.setX( ( viewPos.bottomRight().x() + viewPos.topLeft().x() ) / 2. ); position.setY( ( viewPos.topLeft().y() + viewPos.bottomRight().y() ) / 2. ); position = n->mapFromScene(position); position = n->mapToParent(position); n->setPosition( position.x(), position.y() ); }
void GuiAppInstance::createNodeGui(const NodePtr &node, const NodePtr& parentMultiInstance, const CreateNodeArgs& args) { boost::shared_ptr<NodeCollection> group = node->getGroup(); NodeGraph* graph; if (group) { NodeGraphI* graph_i = group->getNodeGraph(); assert(graph_i); graph = dynamic_cast<NodeGraph*>(graph_i); assert(graph); } else { graph = _imp->_gui->getNodeGraph(); } if (!graph) { throw std::logic_error(""); } NodesGuiList selectedNodes = graph->getSelectedNodes(); NodeGuiPtr nodegui = _imp->_gui->createNodeGUI(node, args); assert(nodegui); if (parentMultiInstance && nodegui) { nodegui->hideGui(); boost::shared_ptr<NodeGuiI> parentNodeGui_i = parentMultiInstance->getNodeGui(); assert(parentNodeGui_i); nodegui->setParentMultiInstance( boost::dynamic_pointer_cast<NodeGui>(parentNodeGui_i) ); } ///It needs to be here because we rely on the _nodeMapping member bool isViewer = node->isEffectViewer() != 0; if (isViewer) { _imp->_gui->createViewerGui(node); } ///must be done after the viewer gui has been created if ( node->isRotoPaintingNode() ) { _imp->_gui->createNewRotoInterface( nodegui.get() ); } if ( ( node->isTrackerNodePlugin() || node->getEffectInstance()->isBuiltinTrackerNode() ) && !parentMultiInstance ) { _imp->_gui->createNewTrackerInterface( nodegui.get() ); } NodeGroup* isGroup = node->isEffectGroup(); if ( isGroup && isGroup->isSubGraphUserVisible() ) { _imp->_gui->createGroupGui(node, args.reason); } ///Don't initialize inputs if it is a multi-instance child since it is not part of the graph if (!parentMultiInstance) { nodegui->initializeInputs(); } if ( (args.reason == eCreateNodeReasonUserCreate) && !isViewer ) { ///we make sure we can have a clean preview. node->computePreviewImage( getTimeLine()->currentFrame() ); triggerAutoSave(); } ///only move main instances if ( node->getParentMultiInstanceName().empty() ) { bool autoConnect = args.reason == eCreateNodeReasonUserCreate; if ( selectedNodes.empty() ) { autoConnect = false; } if ( (args.xPosHint != INT_MIN) && (args.yPosHint != INT_MIN) && (!autoConnect) ) { QPointF pos = nodegui->mapToParent( nodegui->mapFromScene( QPointF(args.xPosHint, args.yPosHint) ) ); nodegui->refreshPosition( pos.x(), pos.y(), true ); } else { BackdropGui* isBd = dynamic_cast<BackdropGui*>( nodegui.get() ); if (!isBd) { NodeGuiPtr selectedNode; if ( (args.reason == eCreateNodeReasonUserCreate) && (selectedNodes.size() == 1) ) { selectedNode = selectedNodes.front(); BackdropGui* isBackdropGui = dynamic_cast<BackdropGui*>( selectedNode.get() ); if (isBackdropGui) { selectedNode.reset(); } } nodegui->getDagGui()->moveNodesForIdealPosition(nodegui, selectedNode, autoConnect); } } } } // createNodeGui
void NodeGraph::moveNodesForIdealPosition(const NodeGuiPtr &node, const NodeGuiPtr &selected, bool autoConnect) { BackdropGuiPtr isBd = toBackdropGui(node); if (isBd) { return; } QRectF viewPos = visibleSceneRect(); ///3 possible values: /// 0 = default , i.e: we pop the node in the middle of the graph's current view /// 1 = pop the node above the selected node and move the inputs of the selected node a little /// 2 = pop the node below the selected node and move the outputs of the selected node a little int behavior = 0; if (!selected || !autoConnect) { behavior = 0; } else { ///this function is redundant with Project::autoConnect, depending on the node selected ///and this node we make some assumptions on to where we could put the node. // 1) selected is output // a) created is output --> fail // b) created is input --> connect input // c) created is regular --> connect input // 2) selected is input // a) created is output --> connect output // b) created is input --> fail // c) created is regular --> connect output // 3) selected is regular // a) created is output--> connect output // b) created is input --> connect input // c) created is regular --> connect output ///1) if ( selected->getNode()->isOutputNode() ) { ///case 1-a) just do default we don't know what else to do if ( node->getNode()->isOutputNode() ) { behavior = 0; } else { ///for either cases 1-b) or 1-c) we just connect the created node as input of the selected node. behavior = 1; } } ///2) and 3) are similar except for case b) else { ///case 2 or 3- a): connect the created node as output of the selected node. if ( node->getNode()->isOutputNode() ) { behavior = 2; } ///case b) else if ( node->getNode()->isInputNode() ) { if ( selected->getNode()->getEffectInstance()->isReader() ) { ///case 2-b) just do default we don't know what else to do behavior = 0; } else { ///case 3-b): connect the created node as input of the selected node behavior = 1; } } ///case c) connect created as output of the selected node else { behavior = 2; } } } NodePtr createdNodeInternal = node->getNode(); NodePtr selectedNodeInternal; if (selected) { selectedNodeInternal = selected->getNode(); } ///if behaviour is 1 , just check that we can effectively connect the node to avoid moving them for nothing ///otherwise fallback on behaviour 0 if (behavior == 1) { const std::vector<NodeWPtr > & inputs = selected->getNode()->getGuiInputs(); bool oneInputEmpty = false; for (std::size_t i = 0; i < inputs.size(); ++i) { if ( !inputs[i].lock() ) { oneInputEmpty = true; break; } } if (!oneInputEmpty) { behavior = 0; } } ProjectPtr proj = getGui()->getApp()->getProject(); ///default QPointF position; if (behavior == 0) { position.setX( ( viewPos.bottomRight().x() + viewPos.topLeft().x() ) / 2. ); position.setY( ( viewPos.topLeft().y() + viewPos.bottomRight().y() ) / 2. ); } else if (behavior == 1) { ///pop it above the selected node ///If this is the first connected input, insert it in a "linear" way so the tree remains vertical int nbConnectedInput = 0; const std::vector<Edge*> & selectedNodeInputs = selected->getInputsArrows(); for (std::vector<Edge*>::const_iterator it = selectedNodeInputs.begin(); it != selectedNodeInputs.end(); ++it) { NodeGuiPtr input; if (*it) { input = (*it)->getSource(); } if (input) { ++nbConnectedInput; } } ///connect it to the first input QSize selectedNodeSize = selected->getSize(); QSize createdNodeSize = node->getSize(); if (nbConnectedInput == 0) { QPointF selectedNodeMiddlePos = selected->scenePos() + QPointF(selectedNodeSize.width() / 2, selectedNodeSize.height() / 2); position.setX(selectedNodeMiddlePos.x() - createdNodeSize.width() / 2); position.setY( selectedNodeMiddlePos.y() - selectedNodeSize.height() / 2 - NodeGui::DEFAULT_OFFSET_BETWEEN_NODES - createdNodeSize.height() ); QRectF createdNodeRect( position.x(), position.y(), createdNodeSize.width(), createdNodeSize.height() ); ///now that we have the position of the node, move the inputs of the selected node to make some space for this node for (std::vector<Edge*>::const_iterator it = selectedNodeInputs.begin(); it != selectedNodeInputs.end(); ++it) { if ( (*it)->hasSource() ) { (*it)->getSource()->moveAbovePositionRecursively(createdNodeRect); } } int selectedInput = selectedNodeInternal->getPreferredInputForConnection(); if (selectedInput != -1) { bool ok = proj->connectNodes(selectedInput, createdNodeInternal, selectedNodeInternal, true); Q_UNUSED(ok); } } else { //ViewerInstancePtr isSelectedViewer = selectedNodeInternal->isEffectViewerInstance(); //Don't pop a dot, it will most likely annoy the user, just fallback on behavior 0 /* position.setX( ( viewPos.bottomRight().x() + viewPos.topLeft().x() ) / 2. ); position.setY( ( viewPos.topLeft().y() + viewPos.bottomRight().y() ) / 2. ); } else { */ QRectF selectedBbox = selected->mapToScene( selected->boundingRect() ).boundingRect(); QPointF selectedCenter = selectedBbox.center(); double y = selectedCenter.y() - selectedNodeSize.height() / 2. - NodeGui::DEFAULT_OFFSET_BETWEEN_NODES - createdNodeSize.height(); double x = selectedCenter.x() + nbConnectedInput * 150; position.setX(x - createdNodeSize.width() / 2.); position.setY(y); int index = selectedNodeInternal->getPreferredInputForConnection(); if (index != -1) { bool ok = proj->connectNodes(index, createdNodeInternal, selectedNodeInternal, true); Q_UNUSED(ok); } //} // if (isSelectedViewer) {*/ } // if (nbConnectedInput == 0) { } else { ///pop it below the selected node const NodesWList& outputs = selectedNodeInternal->getGuiOutputs(); if ( !createdNodeInternal->isOutputNode() || outputs.empty() ) { QSize selectedNodeSize = selected->getSize(); QSize createdNodeSize = node->getSize(); QPointF selectedNodeMiddlePos = selected->scenePos() + QPointF(selectedNodeSize.width() / 2, selectedNodeSize.height() / 2); ///actually move the created node where the selected node is position.setX(selectedNodeMiddlePos.x() - createdNodeSize.width() / 2); position.setY(selectedNodeMiddlePos.y() + (selectedNodeSize.height() / 2) + NodeGui::DEFAULT_OFFSET_BETWEEN_NODES); QRectF createdNodeRect( position.x(), position.y(), createdNodeSize.width(), createdNodeSize.height() ); ///and move the selected node below recusively for (NodesWList::const_iterator it = outputs.begin(); it != outputs.end(); ++it) { NodePtr output = it->lock(); if (!output) { continue; } NodeGuiIPtr output_i = output->getNodeGui(); if (!output_i) { continue; } NodeGuiPtr outputGui = toNodeGui( output_i ); assert(outputGui); if (outputGui) { outputGui->moveBelowPositionRecursively(createdNodeRect); } } ///Connect the created node to the selected node ///finally we connect the created node to the selected node int createdInput = createdNodeInternal->getPreferredInputForConnection(); if (createdInput != -1) { ignore_result( createdNodeInternal->connectInput(selectedNodeInternal, createdInput) ); } if ( !createdNodeInternal->isOutputNode() ) { ///we find all the nodes that were previously connected to the selected node, ///and connect them to the created node instead. std::map<NodePtr, int> outputsConnectedToSelectedNode; selectedNodeInternal->getOutputsConnectedToThisNode(&outputsConnectedToSelectedNode); for (std::map<NodePtr, int>::iterator it = outputsConnectedToSelectedNode.begin(); it != outputsConnectedToSelectedNode.end(); ++it) { if ( it->first->getParentMultiInstanceName().empty() && (it->first != createdNodeInternal) ) { /* Internal rotopaint nodes are connecting to the Rotopaint itself... make sure not to connect internal nodes of the tree */ RotoDrawableItemPtr stroke = it->first->getAttachedRotoItem(); if ( stroke && (stroke->getContext()->getNode() == selectedNodeInternal) ) { continue; } ignore_result( it->first->replaceInput(createdNodeInternal, it->second) ); // bool ok = proj->disconnectNodes(selectedNodeInternal.get(), it->first); // if (ok) { // ignore_result(proj->connectNodes(it->second, createdNodeInternal, it->first)); // } //assert(ok); Might not be ok if the disconnectNodes() action above was queued } } } } else { ///the created node is an output node and the selected node already has several outputs, create it aside QSize createdNodeSize = node->getSize(); QRectF selectedBbox = selected->mapToScene( selected->boundingRect() ).boundingRect(); QPointF selectedCenter = selectedBbox.center(); double y = selectedCenter.y() + selectedBbox.height() / 2. + NodeGui::DEFAULT_OFFSET_BETWEEN_NODES; double x = selectedCenter.x() + (int)outputs.size() * 150; position.setX(x - createdNodeSize.width() / 2.); position.setY(y); //Don't pop a dot, it will most likely annoy the user, just fallback on behavior 0 int index = createdNodeInternal->getPreferredInputForConnection(); bool ok = proj->connectNodes(index, selectedNodeInternal, createdNodeInternal, true); Q_UNUSED(ok); } } position = node->mapFromScene(position); position = node->mapToParent(position); node->setPosition( position.x(), position.y() ); } // moveNodesForIdealPosition
void GuiAppInstance::createNodeGui(const NodePtr &node, const NodePtr& parentMultiInstance, const CreateNodeArgs& args) { NodeCollectionPtr group = node->getGroup(); NodeGraph* graph; if (group) { NodeGraphI* graph_i = group->getNodeGraph(); assert(graph_i); graph = dynamic_cast<NodeGraph*>(graph_i); assert(graph); } else { graph = _imp->_gui->getNodeGraph(); } if (!graph) { throw std::logic_error(""); } NodesGuiList selectedNodes = graph->getSelectedNodes(); NodeGuiPtr nodegui = _imp->_gui->createNodeGUI(node, args); assert(nodegui); if (parentMultiInstance && nodegui) { nodegui->hideGui(); NodeGuiIPtr parentNodeGui_i = parentMultiInstance->getNodeGui(); assert(parentNodeGui_i); nodegui->setParentMultiInstance( boost::dynamic_pointer_cast<NodeGui>(parentNodeGui_i) ); } bool isViewer = node->isEffectViewerInstance() != 0; if (isViewer) { _imp->_gui->createViewerGui(node); } // Must be done after the viewer gui has been created _imp->_gui->createNodeViewerInterface(nodegui); NodeGroupPtr isGroup = node->isEffectNodeGroup(); if ( isGroup && isGroup->isSubGraphUserVisible() ) { _imp->_gui->createGroupGui(node, args); } ///Don't initialize inputs if it is a multi-instance child since it is not part of the graph if (!parentMultiInstance) { nodegui->initializeInputs(); } NodeSerializationPtr serialization = args.getProperty<NodeSerializationPtr >(kCreateNodeArgsPropNodeSerialization); if ( !serialization && !isViewer ) { ///we make sure we can have a clean preview. node->computePreviewImage( getTimeLine()->currentFrame() ); triggerAutoSave(); } ///only move main instances if ( node->getParentMultiInstanceName().empty() && !serialization) { bool autoConnect = args.getProperty<bool>(kCreateNodeArgsPropAutoConnect); if ( selectedNodes.empty() || serialization) { autoConnect = false; } double xPosHint = serialization ? INT_MIN : args.getProperty<double>(kCreateNodeArgsPropNodeInitialPosition, 0); double yPosHint = serialization ? INT_MIN : args.getProperty<double>(kCreateNodeArgsPropNodeInitialPosition, 1); if ( (xPosHint != INT_MIN) && (yPosHint != INT_MIN) && (!autoConnect) ) { QPointF pos = nodegui->mapToParent( nodegui->mapFromScene( QPointF(xPosHint, yPosHint) ) ); nodegui->refreshPosition( pos.x(), pos.y(), true ); } else { BackdropGuiPtr isBd = toBackdropGui(nodegui); if (!isBd) { NodeGuiPtr selectedNode; if ( !serialization && (selectedNodes.size() == 1) ) { selectedNode = selectedNodes.front(); BackdropGuiPtr isBdGui = toBackdropGui(selectedNode); if (isBdGui) { selectedNode.reset(); } } nodegui->getDagGui()->moveNodesForIdealPosition(nodegui, selectedNode, autoConnect); } } } } // createNodeGui