nodeview: updates to complete new context paradigm
This commit is contained in:
+68
-24
@@ -1622,15 +1622,28 @@ void Node::GenerateFrame(FramePtr frame, const GenerateJob &job) const
|
|||||||
Q_UNUSED(job)
|
Q_UNUSED(job)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Node::OutputsTo(Node *n, bool recursively) const
|
bool Node::OutputsTo(Node *n, bool recursively, const OutputConnections &ignore_edges, const OutputConnection &added_edge) const
|
||||||
{
|
{
|
||||||
for (const OutputConnection& conn : output_connections_) {
|
for (const OutputConnection& conn : output_connections_) {
|
||||||
|
if (std::find(ignore_edges.cbegin(), ignore_edges.cend(), conn) != ignore_edges.cend()) {
|
||||||
|
// If this edge is in the "ignore edges" list, skip it
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Node* connected = conn.second.node();
|
Node* connected = conn.second.node();
|
||||||
|
|
||||||
if (connected == n) {
|
if (connected == n) {
|
||||||
return true;
|
return true;
|
||||||
} else if (recursively && connected->OutputsTo(n, recursively)) {
|
} else if (recursively && connected->OutputsTo(n, recursively, ignore_edges, added_edge)) {
|
||||||
return true;
|
return true;
|
||||||
|
} else if (added_edge.first.node() == this) {
|
||||||
|
Node *proposed_connected = added_edge.second.node();
|
||||||
|
|
||||||
|
if (proposed_connected == n) {
|
||||||
|
return true;
|
||||||
|
} else if (recursively && proposed_connected->OutputsTo(n, recursively, ignore_edges, added_edge)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1697,7 +1710,7 @@ bool Node::InputsFrom(const QString &id, bool recursively) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Node::GetRoutesTo(Node *n) const
|
int Node::GetNumberOfRoutesTo(Node *n) const
|
||||||
{
|
{
|
||||||
bool outputs_directly = false;
|
bool outputs_directly = false;
|
||||||
int routes = 0;
|
int routes = 0;
|
||||||
@@ -1708,7 +1721,7 @@ int Node::GetRoutesTo(Node *n) const
|
|||||||
if (connected_node == n) {
|
if (connected_node == n) {
|
||||||
outputs_directly = true;
|
outputs_directly = true;
|
||||||
} else {
|
} else {
|
||||||
routes += connected_node->GetRoutesTo(n);
|
routes += connected_node->GetNumberOfRoutesTo(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2264,25 +2277,29 @@ void NodeSetPositionAndShiftSurroundingsCommand::redo()
|
|||||||
commands_.append(set_pos_command);
|
commands_.append(set_pos_command);
|
||||||
|
|
||||||
// Get bounding rect
|
// Get bounding rect
|
||||||
QRectF bounding_rect(position_.x() - 0.5, position_.y() - 0.5, 1, 1);
|
qreal bounding_rect_sz = 1.0;
|
||||||
|
qreal bounding_rect_half_sz = bounding_rect_sz * 0.5;
|
||||||
|
QRectF bounding_rect(position_.x() - bounding_rect_half_sz, position_.y() - bounding_rect_half_sz, bounding_rect_sz, bounding_rect_sz);
|
||||||
|
|
||||||
// Start moving other nodes
|
// Start moving other nodes
|
||||||
foreach (Node* surrounding, node_->parent()->nodes()) {
|
foreach (Node* surrounding, node_->parent()->nodes()) {
|
||||||
QPointF surrounding_position = node_->parent()->GetNodePosition(surrounding, relative_);
|
if (surrounding != node_) {
|
||||||
if (bounding_rect.contains(surrounding_position) && surrounding != node_) {
|
QPointF surrounding_position = node_->parent()->GetNodePosition(surrounding, relative_);
|
||||||
QPointF new_pos = surrounding_position;
|
if (bounding_rect.contains(surrounding_position)) {
|
||||||
|
QPointF new_pos = surrounding_position;
|
||||||
|
|
||||||
qreal move_rate = 0.50;
|
qreal move_rate = 0.50;
|
||||||
|
|
||||||
if (surrounding_position.y() < position_.y()) {
|
if (surrounding_position.y() < position_.y()) {
|
||||||
move_rate = -move_rate;
|
move_rate = -move_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_pos.setY(new_pos.y() + move_rate);
|
||||||
|
|
||||||
|
auto sur_command = new NodeSetPositionAndShiftSurroundingsCommand(surrounding, relative_, new_pos, true);
|
||||||
|
sur_command->redo();
|
||||||
|
commands_.append(sur_command);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_pos.setY(new_pos.y() + move_rate);
|
|
||||||
|
|
||||||
auto sur_command = new NodeSetPositionAndShiftSurroundingsCommand(surrounding, relative_, new_pos, true);
|
|
||||||
sur_command->redo();
|
|
||||||
commands_.append(sur_command);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -2294,20 +2311,19 @@ void NodeSetPositionAndShiftSurroundingsCommand::redo()
|
|||||||
|
|
||||||
void NodeSetPositionCommand::redo()
|
void NodeSetPositionCommand::redo()
|
||||||
{
|
{
|
||||||
NodeGraph* graph = node_->parent();
|
graph_ = node_->parent();
|
||||||
if (!(added_ = !graph->NodeMapContainsNode(node_, relevant_))) {
|
if (!(added_ = !graph_->NodeMapContainsNode(node_, relevant_))) {
|
||||||
old_pos_ = graph->GetNodePosition(node_, relevant_);
|
old_pos_ = graph_->GetNodePosition(node_, relevant_);
|
||||||
}
|
}
|
||||||
graph->SetNodePosition(node_, relevant_, pos_);
|
graph_->SetNodePosition(node_, relevant_, pos_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeSetPositionCommand::undo()
|
void NodeSetPositionCommand::undo()
|
||||||
{
|
{
|
||||||
NodeGraph* graph = node_->parent();
|
|
||||||
if (added_) {
|
if (added_) {
|
||||||
graph->RemoveNodePosition(node_, relevant_);
|
graph_->RemoveNodePosition(node_, relevant_);
|
||||||
} else {
|
} else {
|
||||||
graph->SetNodePosition(node_, relevant_, old_pos_);
|
graph_->SetNodePosition(node_, relevant_, old_pos_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2368,4 +2384,32 @@ void NodeRemovePositionFromContextCommand::undo()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NodeRemovePositionFromAllContextsCommand::redo()
|
||||||
|
{
|
||||||
|
NodeGraph *graph = node_->parent();
|
||||||
|
|
||||||
|
if (points_.empty()) {
|
||||||
|
// No points yet, let's see what points we should remove
|
||||||
|
auto map = graph->GetPositionMap();
|
||||||
|
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||||
|
if (it.value().contains(node_)) {
|
||||||
|
points_.insert({it.key(), it.value().value(node_)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it=points_.cbegin(); it!=points_.cend(); it++) {
|
||||||
|
graph->RemoveNodePosition(node_, it->first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeRemovePositionFromAllContextsCommand::undo()
|
||||||
|
{
|
||||||
|
NodeGraph *graph = node_->parent();
|
||||||
|
|
||||||
|
for (auto it=points_.crbegin(); it!=points_.crend(); it++) {
|
||||||
|
graph->SetNodePosition(node_, it->first, it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-4
@@ -557,7 +557,8 @@ public:
|
|||||||
* Whether to keep traversing down outputs to find this node (TRUE) or stick to immediate outputs
|
* Whether to keep traversing down outputs to find this node (TRUE) or stick to immediate outputs
|
||||||
* (FALSE).
|
* (FALSE).
|
||||||
*/
|
*/
|
||||||
bool OutputsTo(Node* n, bool recursively) const;
|
bool OutputsTo(Node* n, bool recursively, const OutputConnections &ignore_edges = OutputConnections(), const OutputConnection &added_edge = OutputConnection()) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Same as OutputsTo(Node*), but for a node ID rather than a specific instance.
|
* @brief Same as OutputsTo(Node*), but for a node ID rather than a specific instance.
|
||||||
*/
|
*/
|
||||||
@@ -581,7 +582,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Determines how many paths go from this node out to another node
|
* @brief Determines how many paths go from this node out to another node
|
||||||
*/
|
*/
|
||||||
int GetRoutesTo(Node* n) const;
|
int GetNumberOfRoutesTo(Node* n) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Severs all input and output connections
|
* @brief Severs all input and output connections
|
||||||
@@ -1327,6 +1328,7 @@ private:
|
|||||||
QPointF old_pos_;
|
QPointF old_pos_;
|
||||||
bool added_;
|
bool added_;
|
||||||
bool move_deps_;
|
bool move_deps_;
|
||||||
|
NodeGraph *graph_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1375,7 +1377,7 @@ private:
|
|||||||
class NodeSetPositionAsChildCommand : public UndoCommand
|
class NodeSetPositionAsChildCommand : public UndoCommand
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NodeSetPositionAsChildCommand(Node* node, Node* parent, Node *relative, int this_index, int child_count, bool shift_surroundings) :
|
NodeSetPositionAsChildCommand(Node* node, Node* parent, Node *relative, double this_index, int child_count, bool shift_surroundings) :
|
||||||
node_(node),
|
node_(node),
|
||||||
parent_(parent),
|
parent_(parent),
|
||||||
relative_(relative),
|
relative_(relative),
|
||||||
@@ -1408,7 +1410,7 @@ private:
|
|||||||
Node* parent_;
|
Node* parent_;
|
||||||
Node *relative_;
|
Node *relative_;
|
||||||
|
|
||||||
int this_index_;
|
double this_index_;
|
||||||
int child_count_;
|
int child_count_;
|
||||||
|
|
||||||
bool shift_surroundings_;
|
bool shift_surroundings_;
|
||||||
@@ -1417,6 +1419,25 @@ private:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NodePositionCloseChildGapCommand : public UndoCommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NodePositionCloseChildGapCommand(Node *parent, void *relative, int remove_index, int child_count, bool shift_surroundings);
|
||||||
|
|
||||||
|
virtual Project * GetRelevantProject() const override
|
||||||
|
{
|
||||||
|
return parent_->project();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void redo() override;
|
||||||
|
|
||||||
|
virtual void undo() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Node *parent_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class NodeSetPositionToOffsetOfAnotherNodeCommand : public UndoCommand
|
class NodeSetPositionToOffsetOfAnotherNodeCommand : public UndoCommand
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -1474,6 +1495,30 @@ private:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NodeRemovePositionFromAllContextsCommand : public UndoCommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NodeRemovePositionFromAllContextsCommand(Node *node) :
|
||||||
|
node_(node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Project * GetRelevantProject() const override
|
||||||
|
{
|
||||||
|
return node_->project();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void redo() override;
|
||||||
|
|
||||||
|
virtual void undo() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Node *node_;
|
||||||
|
|
||||||
|
std::map<Node *, QPointF> points_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // NODE_H
|
#endif // NODE_H
|
||||||
|
|||||||
@@ -149,10 +149,10 @@ void NodeView::SetGraph(NodeGraph *graph, const QVector<Node*> &nodes)
|
|||||||
AddNodePosition(it.key(), n);
|
AddNodePosition(it.key(), n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Center on something
|
|
||||||
CenterOnItemsBoundingRect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Center on something
|
||||||
|
QMetaObject::invokeMethod(this, &NodeView::CenterOnItemsBoundingRect, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,14 +171,25 @@ void NodeView::DeleteSelected()
|
|||||||
MultiUndoCommand* command = new MultiUndoCommand();
|
MultiUndoCommand* command = new MultiUndoCommand();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
// First remove any selected edges
|
||||||
QVector<NodeViewEdge *> selected_edges = scene_.GetSelectedEdges();
|
QVector<NodeViewEdge *> selected_edges = scene_.GetSelectedEdges();
|
||||||
|
|
||||||
foreach (NodeViewEdge* edge, selected_edges) {
|
if (!selected_edges.isEmpty()) {
|
||||||
command->add_child(new NodeEdgeRemoveCommand(edge->output(), edge->input()));
|
Node::OutputConnections removed_connections(selected_edges.size());
|
||||||
|
|
||||||
|
for (int i=0; i<selected_edges.size(); i++) {
|
||||||
|
NodeViewEdge* edge = selected_edges.at(i);
|
||||||
|
command->add_child(new NodeEdgeRemoveCommand(edge->output(), edge->input()));
|
||||||
|
removed_connections[i] = {edge->output(), edge->input()};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update contexts
|
||||||
|
UpdateContextsFromEdgeRemove(command, removed_connections);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
// Secondly remove any nodes
|
||||||
QVector<Node*> selected_nodes = scene_.GetSelectedNodes();
|
QVector<Node*> selected_nodes = scene_.GetSelectedNodes();
|
||||||
|
|
||||||
// Ensure no nodes are "undeletable"
|
// Ensure no nodes are "undeletable"
|
||||||
@@ -408,17 +419,69 @@ void NodeView::ZoomOut()
|
|||||||
|
|
||||||
void NodeView::keyPressEvent(QKeyEvent *event)
|
void NodeView::keyPressEvent(QKeyEvent *event)
|
||||||
{
|
{
|
||||||
super::keyPressEvent(event);
|
switch (event->key()) {
|
||||||
|
case Qt::Key_Left:
|
||||||
|
case Qt::Key_Right:
|
||||||
|
case Qt::Key_Up:
|
||||||
|
case Qt::Key_Down:
|
||||||
|
{
|
||||||
|
if (graph_) {
|
||||||
|
MultiUndoCommand *pos_command = new MultiUndoCommand();
|
||||||
|
foreach (Node *n, selected_nodes_) {
|
||||||
|
foreach (Node *context, filter_nodes_) {
|
||||||
|
if (graph_->GetNodesForContext(context).contains(n)) {
|
||||||
|
QPointF old_pos = graph_->GetNodePosition(n, context);
|
||||||
|
|
||||||
if (event->key() == Qt::Key_Escape && !attached_items_.isEmpty()) {
|
// Determine one pixel in scene units
|
||||||
DetachItemsFromCursor();
|
double movement_amt = 1.0 / scale_;
|
||||||
|
|
||||||
// We undo the last action which SHOULD be adding the node
|
// Translate to 2D movement
|
||||||
if (paste_command_) {
|
QPointF node_movement;
|
||||||
paste_command_->undo();
|
switch (event->key()) {
|
||||||
delete paste_command_;
|
case Qt::Key_Left:
|
||||||
paste_command_ = nullptr;
|
node_movement.setX(-movement_amt);
|
||||||
|
break;
|
||||||
|
case Qt::Key_Right:
|
||||||
|
node_movement.setX(movement_amt);
|
||||||
|
break;
|
||||||
|
case Qt::Key_Up:
|
||||||
|
node_movement.setY(-movement_amt);
|
||||||
|
break;
|
||||||
|
case Qt::Key_Down:
|
||||||
|
node_movement.setY(movement_amt);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate from screen units into node units
|
||||||
|
node_movement = NodeViewItem::ScreenToNodePoint(node_movement, scene_.GetFlowDirection());
|
||||||
|
|
||||||
|
// Move command
|
||||||
|
pos_command->add_child(new NodeSetPositionCommand(n, context, old_pos + node_movement, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Core::instance()->undo_stack()->pushIfHasChildren(pos_command);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Qt::Key_Escape:
|
||||||
|
if (!attached_items_.isEmpty()) {
|
||||||
|
DetachItemsFromCursor();
|
||||||
|
|
||||||
|
// We undo the last action which SHOULD be adding the node
|
||||||
|
if (paste_command_) {
|
||||||
|
paste_command_->undo();
|
||||||
|
delete paste_command_;
|
||||||
|
paste_command_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fall through */
|
||||||
|
default:
|
||||||
|
super::keyPressEvent(event);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,19 +489,20 @@ void NodeView::mousePressEvent(QMouseEvent *event)
|
|||||||
{
|
{
|
||||||
if (HandPress(event)) return;
|
if (HandPress(event)) return;
|
||||||
|
|
||||||
QGraphicsItem* item = itemAt(event->pos());
|
|
||||||
|
|
||||||
if (event->button() == Qt::LeftButton) {
|
if (event->button() == Qt::LeftButton) {
|
||||||
NodeViewEdge* edge_item = dynamic_cast<NodeViewEdge*>(item);
|
foreach (NodeViewEdge *edge_item, scene_.edges()) {
|
||||||
if (edge_item && edge_item->arrow_bounding_rect().contains(mapToScene(event->pos()))) {
|
if (edge_item->arrow_bounding_rect().contains(mapToScene(event->pos()))) {
|
||||||
create_edge_src_ = scene_.NodeToUIObject(edge_item->output().node());
|
create_edge_src_ = scene_.NodeToUIObject(edge_item->output().node());
|
||||||
create_edge_src_output_ = edge_item->output().output();
|
create_edge_src_output_ = edge_item->output().output();
|
||||||
create_edge_ = edge_item;
|
create_edge_ = edge_item;
|
||||||
create_edge_already_exists_ = true;
|
create_edge_already_exists_ = true;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QGraphicsItem* item = itemAt(event->pos());
|
||||||
|
|
||||||
if (event->button() == Qt::RightButton) {
|
if (event->button() == Qt::RightButton) {
|
||||||
if (!item || !item->isSelected()) {
|
if (!item || !item->isSelected()) {
|
||||||
// Qt doesn't do this by default for some reason
|
// Qt doesn't do this by default for some reason
|
||||||
@@ -623,13 +687,26 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
|
|||||||
if (HandRelease(event)) return;
|
if (HandRelease(event)) return;
|
||||||
|
|
||||||
if (create_edge_) {
|
if (create_edge_) {
|
||||||
|
// We are creating a new edge or moving an existing one
|
||||||
MultiUndoCommand* command = new MultiUndoCommand();
|
MultiUndoCommand* command = new MultiUndoCommand();
|
||||||
|
|
||||||
|
Node::OutputConnections removed_edges;
|
||||||
|
Node::OutputConnection added_edge;
|
||||||
|
|
||||||
|
bool reconnected_to_itself = false;
|
||||||
|
|
||||||
if (create_edge_already_exists_) {
|
if (create_edge_already_exists_) {
|
||||||
if (!create_edge_->IsConnected()) {
|
if (create_edge_dst_input_ == create_edge_->input()) {
|
||||||
|
reconnected_to_itself = true;
|
||||||
|
} else {
|
||||||
|
// We are moving (or removing) an existing edge
|
||||||
command->add_child(new NodeEdgeRemoveCommand(create_edge_->output(), create_edge_->input()));
|
command->add_child(new NodeEdgeRemoveCommand(create_edge_->output(), create_edge_->input()));
|
||||||
|
|
||||||
|
// Update contexts for edge removal
|
||||||
|
removed_edges.push_back({create_edge_->output(), create_edge_->input()});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// We're creating a new edge, which means this UI object is only temporary
|
||||||
delete create_edge_;
|
delete create_edge_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -645,15 +722,37 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
|
|||||||
create_edge_dst_->setZValue(0);
|
create_edge_dst_->setZValue(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (create_edge_dst_input_.IsValid()) {
|
NodeInput &creating_input = create_edge_dst_input_;
|
||||||
|
if (creating_input.IsValid()) {
|
||||||
// Make connection
|
// Make connection
|
||||||
command->add_child(new NodeEdgeAddCommand(NodeOutput(create_edge_src_->GetNode(), create_edge_src_output_), create_edge_dst_input_));
|
if (!reconnected_to_itself) {
|
||||||
create_edge_dst_input_.Reset();
|
NodeOutput creating_output(create_edge_src_->GetNode(), create_edge_src_output_);
|
||||||
|
|
||||||
|
if (creating_input.IsConnected()) {
|
||||||
|
Node::OutputConnection existing_edge_to_remove = {creating_input.GetConnectedOutput(), creating_input};
|
||||||
|
command->add_child(new NodeEdgeRemoveCommand(existing_edge_to_remove.first, existing_edge_to_remove.second));
|
||||||
|
removed_edges.push_back(existing_edge_to_remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
command->add_child(new NodeEdgeAddCommand(creating_output, creating_input));
|
||||||
|
added_edge = {creating_output, creating_input};
|
||||||
|
}
|
||||||
|
|
||||||
|
creating_input.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
create_edge_dst_ = nullptr;
|
create_edge_dst_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update contexts
|
||||||
|
if (!removed_edges.empty()) {
|
||||||
|
UpdateContextsFromEdgeRemove(command, removed_edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (added_edge.first.IsValid()) {
|
||||||
|
UpdateContextsFromEdgeAdd(command, added_edge, removed_edges);
|
||||||
|
}
|
||||||
|
|
||||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -668,44 +767,105 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
|
|||||||
command->add_child(paste_command_);
|
command->add_child(paste_command_);
|
||||||
paste_command_ = nullptr;
|
paste_command_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attached_items_.size() == 1) {
|
|
||||||
Node* dropping_node = attached_items_.first().item->GetNode();
|
|
||||||
|
|
||||||
if (drop_edge_) {
|
|
||||||
// Remove old edge
|
|
||||||
command->add_child(new NodeEdgeRemoveCommand(drop_edge_->output(), drop_edge_->input()));
|
|
||||||
|
|
||||||
// Place new edges
|
|
||||||
command->add_child(new NodeEdgeAddCommand(drop_edge_->output(), drop_input_));
|
|
||||||
command->add_child(new NodeEdgeAddCommand(dropping_node, drop_edge_->input()));
|
|
||||||
}
|
|
||||||
|
|
||||||
drop_edge_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
DetachItemsFromCursor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it=positions_.begin(); it!=positions_.end(); it++) {
|
{
|
||||||
NodeViewItem *item = it.key();
|
// If any node positions changed, set them in their contexts now
|
||||||
Position &pos_data = it.value();
|
MultiUndoCommand *set_pos_command = new MultiUndoCommand();
|
||||||
QPointF current_item_pos = item->GetNodePosition();
|
for (auto it=positions_.begin(); it!=positions_.end(); it++) {
|
||||||
Node *node = pos_data.node;
|
NodeViewItem *item = it.key();
|
||||||
|
Position &pos_data = it.value();
|
||||||
|
QPointF current_item_pos = item->GetNodePosition();
|
||||||
|
Node *node = pos_data.node;
|
||||||
|
|
||||||
if (pos_data.original_item_pos != current_item_pos) {
|
if (pos_data.original_item_pos != current_item_pos) {
|
||||||
QPointF diff = current_item_pos - pos_data.original_item_pos;
|
QPointF diff = current_item_pos - pos_data.original_item_pos;
|
||||||
|
|
||||||
foreach (Node *context, filter_nodes_) {
|
foreach (Node *context, filter_nodes_) {
|
||||||
if (graph_->ContextContainsNode(node, context)) {
|
if (graph_->ContextContainsNode(node, context)) {
|
||||||
QPointF current_node_pos_in_context = graph_->GetNodePosition(node, context);
|
QPointF current_node_pos_in_context = graph_->GetNodePosition(node, context);
|
||||||
current_node_pos_in_context += diff;
|
current_node_pos_in_context += diff;
|
||||||
command->add_child(new NodeSetPositionCommand(node, context, current_node_pos_in_context, false));
|
set_pos_command->add_child(new NodeSetPositionCommand(node, context, current_node_pos_in_context, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos_data.original_item_pos = current_item_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (set_pos_command->child_count()) {
|
||||||
|
set_pos_command->redo();
|
||||||
|
command->add_child(set_pos_command);
|
||||||
|
} else {
|
||||||
|
delete set_pos_command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!attached_items_.isEmpty()) {
|
||||||
|
{
|
||||||
|
// Dropped attached item onto an edge, connect it between them
|
||||||
|
MultiUndoCommand *drop_edge_command = new MultiUndoCommand();
|
||||||
|
if (attached_items_.size() == 1) {
|
||||||
|
Node* dropping_node = attached_items_.first().item->GetNode();
|
||||||
|
|
||||||
|
if (drop_edge_) {
|
||||||
|
// Remove old edge
|
||||||
|
drop_edge_command->add_child(new NodeEdgeRemoveCommand(drop_edge_->output(), drop_edge_->input()));
|
||||||
|
|
||||||
|
// Place new edges
|
||||||
|
drop_edge_command->add_child(new NodeEdgeAddCommand(drop_edge_->output(), drop_input_));
|
||||||
|
drop_edge_command->add_child(new NodeEdgeAddCommand(dropping_node, drop_edge_->input()));
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_edge_ = nullptr;
|
||||||
|
}
|
||||||
|
if (drop_edge_command->child_count()) {
|
||||||
|
drop_edge_command->redo();
|
||||||
|
command->add_child(drop_edge_command);
|
||||||
|
} else {
|
||||||
|
delete drop_edge_command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Remove from context any nodes that don't specifically output to said context
|
||||||
|
MultiUndoCommand *remove_pos_command = new MultiUndoCommand();
|
||||||
|
|
||||||
|
foreach (const AttachedItem &attached, attached_items_) {
|
||||||
|
MultiUndoCommand *remove_pos_subcommand = new MultiUndoCommand();
|
||||||
|
Node *attached_node = scene_.item_map().key(attached.item);
|
||||||
|
|
||||||
|
bool removed = false;
|
||||||
|
QVector<Node*> relevant_contexts;
|
||||||
|
foreach (Node *context, filter_nodes_) {
|
||||||
|
if (attached_node->OutputsTo(context, true)) {
|
||||||
|
relevant_contexts.append(context);
|
||||||
|
} else {
|
||||||
|
remove_pos_subcommand->add_child(new NodeRemovePositionFromContextCommand(attached_node, context));
|
||||||
|
removed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removed && !relevant_contexts.isEmpty()) {
|
||||||
|
foreach (Node *relevant, relevant_contexts) {
|
||||||
|
remove_pos_subcommand->add_child(new NodeSetPositionCommand(attached_node, relevant, GetEstimatedPositionForContext(attached.item, relevant), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_pos_command->add_child(remove_pos_subcommand);
|
||||||
|
} else {
|
||||||
|
delete remove_pos_subcommand;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pos_data.original_item_pos = current_item_pos;
|
if (remove_pos_command->child_count()) {
|
||||||
|
remove_pos_command->redo();
|
||||||
|
command->add_child(remove_pos_command);
|
||||||
|
} else {
|
||||||
|
delete remove_pos_command;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DetachItemsFromCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||||
@@ -991,19 +1151,29 @@ void NodeView::RemoveNodePosition(Node *node, Node *relative)
|
|||||||
{
|
{
|
||||||
if (filter_mode_ == kFilterShowSelective) {
|
if (filter_mode_ == kFilterShowSelective) {
|
||||||
if (filter_nodes_.contains(relative)) {
|
if (filter_nodes_.contains(relative)) {
|
||||||
// Determine if any other contexts have this node
|
NodeViewItem *item = scene_.item_map().value(node);
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
foreach (Node *context, filter_nodes_) {
|
if (item && !item->GetPreventRemoving()) {
|
||||||
if (graph_->ContextContainsNode(node, context)) {
|
// Determine if any other contexts have this node
|
||||||
found = true;
|
bool found = false;
|
||||||
break;
|
|
||||||
|
foreach (Node *context, filter_nodes_) {
|
||||||
|
if (graph_->ContextContainsNode(node, context)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
positions_.remove(scene_.item_map().value(node));
|
foreach (const Node::OutputConnection &oc, node->output_connections()) {
|
||||||
scene_.RemoveNode(node);
|
scene_.RemoveEdge(oc.first, oc.second);
|
||||||
|
}
|
||||||
|
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||||
|
scene_.RemoveEdge(it->second, it->first);
|
||||||
|
}
|
||||||
|
positions_.remove(item);
|
||||||
|
scene_.RemoveNode(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1098,6 +1268,22 @@ void NodeView::ZoomIntoCursorPosition(QWheelEvent *event, double multiplier, con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NodeView::event(QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::ShortcutOverride) {
|
||||||
|
QKeyEvent *se = static_cast<QKeyEvent *>(event);
|
||||||
|
if (se->key() == Qt::Key_Left
|
||||||
|
|| se->key() == Qt::Key_Right
|
||||||
|
|| se->key() == Qt::Key_Up
|
||||||
|
|| se->key() == Qt::Key_Down) {
|
||||||
|
se->accept();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super::event(event);
|
||||||
|
}
|
||||||
|
|
||||||
void NodeView::ZoomFromKeyboard(double multiplier)
|
void NodeView::ZoomFromKeyboard(double multiplier)
|
||||||
{
|
{
|
||||||
QPoint cursor_pos = mapFromGlobal(QCursor::pos());
|
QPoint cursor_pos = mapFromGlobal(QCursor::pos());
|
||||||
@@ -1110,6 +1296,149 @@ void NodeView::ZoomFromKeyboard(double multiplier)
|
|||||||
ZoomIntoCursorPosition(nullptr, multiplier, cursor_pos);
|
ZoomIntoCursorPosition(nullptr, multiplier, cursor_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NodeView::DetermineIfNodeIsFloatingInContext(Node *node, Node *context, Node *source, const Node::OutputConnections &removed_edges, const Node::OutputConnection &added_edge)
|
||||||
|
{
|
||||||
|
// Determines whether `node` outputs to another node in `context` besides `source`
|
||||||
|
foreach (const Node::OutputConnection &conn, node->output_connections()) {
|
||||||
|
Node *output_candidate = conn.second.node();
|
||||||
|
|
||||||
|
if (output_candidate == source) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (graph_->ContextContainsNode(output_candidate, context)) {
|
||||||
|
if (!output_candidate->OutputsTo(source, true, removed_edges, added_edge)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeView::UpdateContextsFromEdgeRemove(MultiUndoCommand *command, const Node::OutputConnections &remove_edges)
|
||||||
|
{
|
||||||
|
// For each edge we remove, determine if we should remove the node from a context as well
|
||||||
|
foreach (const Node::OutputConnection &edge, remove_edges) {
|
||||||
|
Node *output_node = edge.first.node();
|
||||||
|
QVector<Node *> contexts_to_remove_from;
|
||||||
|
int contexts_containing = 0;
|
||||||
|
|
||||||
|
for (auto it=graph_->GetPositionMap().cbegin(); it!=graph_->GetPositionMap().cend(); it++) {
|
||||||
|
Node *context = it.key();
|
||||||
|
|
||||||
|
if (it.value().contains(output_node)) {
|
||||||
|
bool currently_outputs = output_node->OutputsTo(context, true);
|
||||||
|
bool will_output_after_operation = output_node->OutputsTo(context, true, remove_edges);
|
||||||
|
|
||||||
|
if (currently_outputs && !will_output_after_operation) {
|
||||||
|
// Will remove
|
||||||
|
contexts_to_remove_from.append(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
contexts_containing++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removing from all current contexts, convert to a floating node (i.e. don't remove from the context)
|
||||||
|
if (contexts_to_remove_from.size() != contexts_containing) {
|
||||||
|
// Not removing from all contexts, can remove
|
||||||
|
bool removing_from_all_current_contexts = true;
|
||||||
|
|
||||||
|
foreach (Node *context, filter_nodes_) {
|
||||||
|
if (graph_->ContextContainsNode(output_node, context)) {
|
||||||
|
if (!contexts_to_remove_from.contains(context)) {
|
||||||
|
removing_from_all_current_contexts = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Node *context, contexts_to_remove_from) {
|
||||||
|
RecursivelyRemoveFloatingNodeFromContext(command, output_node, context, output_node, remove_edges, Node::OutputConnection(), removing_from_all_current_contexts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeView::RecursivelyRemoveFloatingNodeFromContext(MultiUndoCommand *command, Node *node, Node *context, Node *source, const Node::OutputConnections &removed_edges, const Node::OutputConnection &added_edge, bool prevent_removing)
|
||||||
|
{
|
||||||
|
if (prevent_removing) {
|
||||||
|
command->add_child(new NodeViewItemPreventRemovingCommand(this, node, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
command->add_child(new NodeRemovePositionFromContextCommand(node, context));
|
||||||
|
|
||||||
|
// Remove any dependency from the context that's also floating
|
||||||
|
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||||
|
Node *dependency = it->second.node();
|
||||||
|
|
||||||
|
// Determine if this node happens to output to anything else in the context (which may be
|
||||||
|
// another floating node that won't be removed by this operation)
|
||||||
|
if (!DetermineIfNodeIsFloatingInContext(dependency, context, source, removed_edges, added_edge)) {
|
||||||
|
RecursivelyRemoveFloatingNodeFromContext(command, dependency, context, source, removed_edges, added_edge, prevent_removing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeView::RecursivelyAddNodeToContext(MultiUndoCommand *command, Node *node, Node *context)
|
||||||
|
{
|
||||||
|
command->add_child(new NodeSetPositionCommand(node, context, GetEstimatedPositionForContext(scene_.item_map().value(node), context), false));
|
||||||
|
|
||||||
|
// Add dependency
|
||||||
|
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||||
|
Node *dependency = it->second.node();
|
||||||
|
RecursivelyAddNodeToContext(command, dependency, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeView::UpdateContextsFromEdgeAdd(MultiUndoCommand *command, const Node::OutputConnection &added_edge, const Node::OutputConnections &removed_edges)
|
||||||
|
{
|
||||||
|
// Determine if node currently does NOT output to a context that it WILL after this operation
|
||||||
|
QVector<Node*> contexts_to_add_to;
|
||||||
|
Node *connecting_node = added_edge.first.node();
|
||||||
|
Node *input_node = added_edge.second.node();
|
||||||
|
for (auto it=graph_->GetPositionMap().cbegin(); it!=graph_->GetPositionMap().cend(); it++) {
|
||||||
|
if (it.value().contains(input_node)) {
|
||||||
|
contexts_to_add_to.append(it.key());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!contexts_to_add_to.isEmpty()) {
|
||||||
|
// Determine whether the node is currently "floating", i.e. it outputs to none of the contexts
|
||||||
|
// that it currently belongs to. If so, we will take ownership of it with this node.
|
||||||
|
bool node_is_floating = true;
|
||||||
|
QVector<Node*> current_contexts;
|
||||||
|
for (auto it=graph_->GetPositionMap().cbegin(); it!=graph_->GetPositionMap().cend(); it++) {
|
||||||
|
if (it.value().contains(connecting_node)) {
|
||||||
|
if (connecting_node->OutputsTo(it.key(), true, removed_edges)) {
|
||||||
|
node_is_floating = false;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
current_contexts.append(it.key());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node_is_floating) {
|
||||||
|
// This action will unfloat this node, so remove it from all current contexts
|
||||||
|
foreach (Node *context, current_contexts) {
|
||||||
|
RecursivelyRemoveFloatingNodeFromContext(command, connecting_node, context, connecting_node, removed_edges, added_edge, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add nodes to contexts
|
||||||
|
foreach (Node *context, contexts_to_add_to) {
|
||||||
|
RecursivelyAddNodeToContext(command, connecting_node, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF NodeView::GetEstimatedPositionForContext(NodeViewItem *item, Node *context) const
|
||||||
|
{
|
||||||
|
return item->GetNodePosition() - context_offsets_.value(context);
|
||||||
|
}
|
||||||
|
|
||||||
NodeView::NodeViewAttachNodesToCursor::NodeViewAttachNodesToCursor(NodeView *view, const QVector<Node *> &nodes) :
|
NodeView::NodeViewAttachNodesToCursor::NodeViewAttachNodesToCursor(NodeView *view, const QVector<Node *> &nodes) :
|
||||||
view_(view),
|
view_(view),
|
||||||
nodes_(nodes)
|
nodes_(nodes)
|
||||||
@@ -1132,4 +1461,23 @@ Project *NodeView::NodeViewAttachNodesToCursor::GetRelevantProject() const
|
|||||||
return dynamic_cast<Project*>(view_->graph_);
|
return dynamic_cast<Project*>(view_->graph_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NodeView::NodeViewItemPreventRemovingCommand::redo()
|
||||||
|
{
|
||||||
|
NodeViewItem *item = view_->scene_.item_map().value(node_);
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
old_prevent_removing_ = item->GetPreventRemoving();
|
||||||
|
item->SetPreventRemoving(new_prevent_removing_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeView::NodeViewItemPreventRemovingCommand::undo()
|
||||||
|
{
|
||||||
|
NodeViewItem *item = view_->scene_.item_map().value(node_);
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
item->SetPreventRemoving(old_prevent_removing_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ protected:
|
|||||||
|
|
||||||
virtual void ZoomIntoCursorPosition(QWheelEvent *event, double multiplier, const QPointF &cursor_pos) override;
|
virtual void ZoomIntoCursorPosition(QWheelEvent *event, double multiplier, const QPointF &cursor_pos) override;
|
||||||
|
|
||||||
|
virtual bool event(QEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void AttachNodesToCursor(const QVector<Node *> &nodes);
|
void AttachNodesToCursor(const QVector<Node *> &nodes);
|
||||||
|
|
||||||
@@ -107,6 +109,14 @@ private:
|
|||||||
|
|
||||||
void ZoomFromKeyboard(double multiplier);
|
void ZoomFromKeyboard(double multiplier);
|
||||||
|
|
||||||
|
bool DetermineIfNodeIsFloatingInContext(Node *node, Node *context, Node *source, const Node::OutputConnections &removed_edges, const Node::OutputConnection &added_edge);
|
||||||
|
void UpdateContextsFromEdgeRemove(MultiUndoCommand *command, const Node::OutputConnections &remove_edges);
|
||||||
|
void UpdateContextsFromEdgeAdd(MultiUndoCommand *command, const Node::OutputConnection &added_edge, const Node::OutputConnections &removed_edges = Node::OutputConnections());
|
||||||
|
void RecursivelyAddNodeToContext(MultiUndoCommand *command, Node *node, Node *context);
|
||||||
|
void RecursivelyRemoveFloatingNodeFromContext(MultiUndoCommand *command, Node *node, Node *context, Node *source, const Node::OutputConnections &removed_edges, const Node::OutputConnection &added_edge, bool prevent_removing);
|
||||||
|
|
||||||
|
QPointF GetEstimatedPositionForContext(NodeViewItem *item, Node *context) const;
|
||||||
|
|
||||||
class NodeViewAttachNodesToCursor : public UndoCommand
|
class NodeViewAttachNodesToCursor : public UndoCommand
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -132,6 +142,32 @@ private:
|
|||||||
QPointF original_pos;
|
QPointF original_pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NodeViewItemPreventRemovingCommand : public UndoCommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NodeViewItemPreventRemovingCommand(NodeView *view, Node *node, bool prevent_removing) :
|
||||||
|
view_(view),
|
||||||
|
node_(node),
|
||||||
|
new_prevent_removing_(prevent_removing)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual void redo() override;
|
||||||
|
|
||||||
|
virtual void undo() override;
|
||||||
|
|
||||||
|
virtual Project * GetRelevantProject() const override
|
||||||
|
{
|
||||||
|
return node_->project();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
NodeView *view_;
|
||||||
|
Node *node_;
|
||||||
|
bool new_prevent_removing_;
|
||||||
|
bool old_prevent_removing_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
QList<AttachedItem> attached_items_;
|
QList<AttachedItem> attached_items_;
|
||||||
|
|
||||||
NodeViewEdge* drop_edge_;
|
NodeViewEdge* drop_edge_;
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ NodeViewItem::NodeViewItem(QGraphicsItem *parent) :
|
|||||||
expanded_(false),
|
expanded_(false),
|
||||||
hide_titlebar_(false),
|
hide_titlebar_(false),
|
||||||
highlighted_index_(-1),
|
highlighted_index_(-1),
|
||||||
flow_dir_(NodeViewCommon::kLeftToRight)
|
flow_dir_(NodeViewCommon::kLeftToRight),
|
||||||
|
prevent_removing_(false)
|
||||||
{
|
{
|
||||||
// Set flags for this widget
|
// Set flags for this widget
|
||||||
setFlag(QGraphicsItem::ItemIsMovable);
|
setFlag(QGraphicsItem::ItemIsMovable);
|
||||||
@@ -68,31 +69,7 @@ NodeViewItem::NodeViewItem(QGraphicsItem *parent) :
|
|||||||
|
|
||||||
QPointF NodeViewItem::GetNodePosition() const
|
QPointF NodeViewItem::GetNodePosition() const
|
||||||
{
|
{
|
||||||
QPointF node_pos;
|
return ScreenToNodePoint(pos(), flow_dir_);
|
||||||
|
|
||||||
qreal adjusted_x = pos().x() / DefaultItemHorizontalPadding();
|
|
||||||
qreal adjusted_y = pos().y() / DefaultItemVerticalPadding();
|
|
||||||
|
|
||||||
switch (flow_dir_) {
|
|
||||||
case NodeViewCommon::kLeftToRight:
|
|
||||||
node_pos.setX(adjusted_x);
|
|
||||||
node_pos.setY(adjusted_y);
|
|
||||||
break;
|
|
||||||
case NodeViewCommon::kRightToLeft:
|
|
||||||
node_pos.setX(-adjusted_x);
|
|
||||||
node_pos.setY(adjusted_y);
|
|
||||||
break;
|
|
||||||
case NodeViewCommon::kTopToBottom:
|
|
||||||
node_pos.setX(adjusted_y);
|
|
||||||
node_pos.setY(adjusted_x);
|
|
||||||
break;
|
|
||||||
case NodeViewCommon::kBottomToTop:
|
|
||||||
node_pos.setX(-adjusted_y);
|
|
||||||
node_pos.setY(adjusted_x);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return node_pos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeViewItem::SetNodePosition(const QPointF &pos)
|
void NodeViewItem::SetNodePosition(const QPointF &pos)
|
||||||
@@ -122,24 +99,88 @@ int NodeViewItem::DefaultItemBorder()
|
|||||||
return QFontMetrics(QFont()).height() / 12;
|
return QFontMetrics(QFont()).height() / 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal NodeViewItem::DefaultItemHorizontalPadding() const
|
QPointF NodeViewItem::NodeToScreenPoint(QPointF p, NodeViewCommon::FlowDirection direction)
|
||||||
{
|
{
|
||||||
if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal) {
|
switch (direction) {
|
||||||
|
case NodeViewCommon::kLeftToRight:
|
||||||
|
// NodeGraphs are always left-to-right internally, no need to translate
|
||||||
|
break;
|
||||||
|
case NodeViewCommon::kRightToLeft:
|
||||||
|
// Invert X value
|
||||||
|
p.setX(-p.x());
|
||||||
|
break;
|
||||||
|
case NodeViewCommon::kTopToBottom:
|
||||||
|
// Swap X/Y
|
||||||
|
p = QPointF(p.y(), p.x());
|
||||||
|
break;
|
||||||
|
case NodeViewCommon::kBottomToTop:
|
||||||
|
// Swap X/Y and invert Y
|
||||||
|
p = QPointF(p.y(), -p.x());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiply by item sizes for this direction
|
||||||
|
p.setX(p.x() * DefaultItemHorizontalPadding(direction));
|
||||||
|
p.setY(p.y() * DefaultItemVerticalPadding(direction));
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF NodeViewItem::ScreenToNodePoint(QPointF p, NodeViewCommon::FlowDirection direction)
|
||||||
|
{
|
||||||
|
// Divide by item sizes for this direction
|
||||||
|
p.setX(p.x() / DefaultItemHorizontalPadding(direction));
|
||||||
|
p.setY(p.y() / DefaultItemVerticalPadding(direction));
|
||||||
|
|
||||||
|
switch (direction) {
|
||||||
|
case NodeViewCommon::kLeftToRight:
|
||||||
|
// NodeGraphs are always left-to-right internally, no need to translate
|
||||||
|
break;
|
||||||
|
case NodeViewCommon::kRightToLeft:
|
||||||
|
// Invert X value
|
||||||
|
p.setX(-p.x());
|
||||||
|
break;
|
||||||
|
case NodeViewCommon::kTopToBottom:
|
||||||
|
// Swap X/Y
|
||||||
|
p = QPointF(p.y(), p.x());
|
||||||
|
break;
|
||||||
|
case NodeViewCommon::kBottomToTop:
|
||||||
|
// Swap X/Y and invert Y
|
||||||
|
p = QPointF(-p.y(), p.x());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal NodeViewItem::DefaultItemHorizontalPadding(NodeViewCommon::FlowDirection dir)
|
||||||
|
{
|
||||||
|
if (NodeViewCommon::GetFlowOrientation(dir) == Qt::Horizontal) {
|
||||||
return DefaultItemWidth() * 1.5;
|
return DefaultItemWidth() * 1.5;
|
||||||
} else {
|
} else {
|
||||||
return DefaultItemWidth() * 1.25;
|
return DefaultItemWidth() * 1.25;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal NodeViewItem::DefaultItemVerticalPadding() const
|
qreal NodeViewItem::DefaultItemVerticalPadding(NodeViewCommon::FlowDirection dir)
|
||||||
{
|
{
|
||||||
if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal) {
|
if (NodeViewCommon::GetFlowOrientation(dir) == Qt::Horizontal) {
|
||||||
return DefaultItemHeight() * 1.5;
|
return DefaultItemHeight() * 1.5;
|
||||||
} else {
|
} else {
|
||||||
return DefaultItemHeight() * 2.0;
|
return DefaultItemHeight() * 2.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qreal NodeViewItem::DefaultItemHorizontalPadding() const
|
||||||
|
{
|
||||||
|
return DefaultItemHorizontalPadding(flow_dir_);
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal NodeViewItem::DefaultItemVerticalPadding() const
|
||||||
|
{
|
||||||
|
return DefaultItemVerticalPadding(flow_dir_);
|
||||||
|
}
|
||||||
|
|
||||||
void NodeViewItem::AddEdge(NodeViewEdge *edge)
|
void NodeViewItem::AddEdge(NodeViewEdge *edge)
|
||||||
{
|
{
|
||||||
edges_.append(edge);
|
edges_.append(edge);
|
||||||
@@ -479,26 +520,7 @@ QPointF NodeViewItem::GetInputPointInternal(int index, const QPointF& source_pos
|
|||||||
|
|
||||||
void NodeViewItem::UpdateNodePosition()
|
void NodeViewItem::UpdateNodePosition()
|
||||||
{
|
{
|
||||||
const QPointF &pos = cached_node_pos_;
|
setPos(NodeToScreenPoint(cached_node_pos_, flow_dir_));
|
||||||
|
|
||||||
switch (flow_dir_) {
|
|
||||||
case NodeViewCommon::kLeftToRight:
|
|
||||||
setPos(pos.x() * DefaultItemHorizontalPadding(),
|
|
||||||
pos.y() * DefaultItemVerticalPadding());
|
|
||||||
break;
|
|
||||||
case NodeViewCommon::kRightToLeft:
|
|
||||||
setPos(-pos.x() * DefaultItemHorizontalPadding(),
|
|
||||||
pos.y() * DefaultItemVerticalPadding());
|
|
||||||
break;
|
|
||||||
case NodeViewCommon::kTopToBottom:
|
|
||||||
setPos(pos.y() * DefaultItemHorizontalPadding(),
|
|
||||||
pos.x() * DefaultItemVerticalPadding());
|
|
||||||
break;
|
|
||||||
case NodeViewCommon::kBottomToTop:
|
|
||||||
setPos(pos.y() * DefaultItemHorizontalPadding(),
|
|
||||||
-pos.x() * DefaultItemVerticalPadding());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,8 +95,12 @@ public:
|
|||||||
|
|
||||||
static int DefaultItemBorder();
|
static int DefaultItemBorder();
|
||||||
|
|
||||||
qreal DefaultItemHorizontalPadding() const;
|
static QPointF NodeToScreenPoint(QPointF p, NodeViewCommon::FlowDirection direction);
|
||||||
|
static QPointF ScreenToNodePoint(QPointF p, NodeViewCommon::FlowDirection direction);
|
||||||
|
|
||||||
|
static qreal DefaultItemHorizontalPadding(NodeViewCommon::FlowDirection dir);
|
||||||
|
static qreal DefaultItemVerticalPadding(NodeViewCommon::FlowDirection dir);
|
||||||
|
qreal DefaultItemHorizontalPadding() const;
|
||||||
qreal DefaultItemVerticalPadding() const;
|
qreal DefaultItemVerticalPadding() const;
|
||||||
|
|
||||||
void AddEdge(NodeViewEdge* edge);
|
void AddEdge(NodeViewEdge* edge);
|
||||||
@@ -111,6 +115,16 @@ public:
|
|||||||
|
|
||||||
void SetHighlightedIndex(int index);
|
void SetHighlightedIndex(int index);
|
||||||
|
|
||||||
|
void SetPreventRemoving(bool e)
|
||||||
|
{
|
||||||
|
prevent_removing_ = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetPreventRemoving() const
|
||||||
|
{
|
||||||
|
return prevent_removing_;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
||||||
|
|
||||||
@@ -174,6 +188,8 @@ private:
|
|||||||
|
|
||||||
QPointF cached_node_pos_;
|
QPointF cached_node_pos_;
|
||||||
|
|
||||||
|
bool prevent_removing_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ int NodeViewScene::DetermineWeight(Node *n)
|
|||||||
int weight = 0;
|
int weight = 0;
|
||||||
|
|
||||||
foreach (Node* i, inputs) {
|
foreach (Node* i, inputs) {
|
||||||
if (i->GetRoutesTo(n) == 1) {
|
if (i->GetNumberOfRoutesTo(n) == 1) {
|
||||||
weight += DetermineWeight(i);
|
weight += DetermineWeight(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -754,9 +754,15 @@ void MainWindow::FocusedPanelChanged(PanelWidget *panel)
|
|||||||
// Signal project panel focus
|
// Signal project panel focus
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
if (project->project()) {
|
if (project->project()) {
|
||||||
QVector<Node*> context = {project->project()->root()};
|
node_panel_->SetGraph(project->project(), {project->project()->root()});
|
||||||
node_panel_->SetGraph(project->project(), context);
|
|
||||||
node_panel_->SelectWithDependencies(context, false);
|
bool center = true;
|
||||||
|
auto selected = project->SelectedItems();
|
||||||
|
if (selected.isEmpty()) {
|
||||||
|
selected.append(project->project()->root());
|
||||||
|
center = false;
|
||||||
|
}
|
||||||
|
node_panel_->Select(selected, center);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user