reimplemented node positioning and deleting

This commit is contained in:
itsmattkc
2021-11-17 12:09:41 -08:00
parent b850a550bd
commit a6af473f53
13 changed files with 255 additions and 137 deletions
+13
View File
@@ -44,6 +44,19 @@ void NodeGraph::Clear()
}
}
int NodeGraph::GetNumberOfContextsNodeIsIn(Node *node, bool except_itself) const
{
int count = 0;
foreach (Node *ctx, node_children_) {
if (ctx->ContextContainsNode(node) && (!except_itself || ctx != node)) {
count++;
}
}
return count;
}
void NodeGraph::childEvent(QChildEvent *event)
{
super::childEvent(event);
+2
View File
@@ -63,6 +63,8 @@ public:
return default_nodes_;
}
int GetNumberOfContextsNodeIsIn(Node *node, bool except_itself = false) const;
signals:
/**
* @brief Signal emitted when a Node is added to the graph
+7 -7
View File
@@ -210,7 +210,7 @@ bool LoadOTIOTask::Run()
block->setParent(sequence->parent());
// Position transition in its own context
sequence->parent()->SetNodePosition(block, block, QPointF(0, 0));
block->SetNodePositionInContext(block, QPointF(0, 0));
}
if (otio_block->schema_name() == "Gap") {
@@ -218,7 +218,7 @@ bool LoadOTIOTask::Run()
block->setParent(sequence->parent());
// Position transition in its own context
sequence->parent()->SetNodePosition(block, block, QPointF(0, 0));
block->SetNodePositionInContext(block, QPointF(0, 0));
}
// Update this after it's used but before any continue statements
@@ -246,7 +246,7 @@ bool LoadOTIOTask::Run()
QFileInfo info(probed_item->filename());
probed_item->SetLabel(info.fileName());
FolderAddChild add(sequence_footage, probed_item, true);
FolderAddChild add(sequence_footage, probed_item);
add.redo_now();
}
@@ -254,10 +254,10 @@ bool LoadOTIOTask::Run()
block->setParent(sequence->parent());
// Position clip in its own context
sequence->parent()->SetNodePosition(block, block, QPointF(0, 0));
block->SetNodePositionInContext(block, QPointF(0, 0));
// Position footage in its context
sequence->parent()->SetNodePosition(probed_item, block, QPointF(-2, 0));
block->SetNodePositionInContext(probed_item, QPointF(-2, 0));
if (track->type() == Track::kVideo) {
@@ -266,14 +266,14 @@ bool LoadOTIOTask::Run()
Node::ConnectEdge(probed_item, NodeInput(transform, TransformDistortNode::kTextureInput));
Node::ConnectEdge(transform, NodeInput(block, ClipBlock::kBufferIn));
sequence->parent()->SetNodePosition(transform, block, QPointF(-1, 0));
block->SetNodePositionInContext(transform, QPointF(-1, 0));
} else {
VolumeNode* volume_node = new VolumeNode();
volume_node->setParent(sequence->parent());
Node::ConnectEdge(probed_item, NodeInput(volume_node, VolumeNode::kSamplesInput));
Node::ConnectEdge(volume_node, NodeInput(block, ClipBlock::kBufferIn));
sequence->parent()->SetNodePosition(volume_node, block, QPointF(-1, 0));
block->SetNodePositionInContext(volume_node, QPointF(-1, 0));
}
}
}
+15 -37
View File
@@ -125,43 +125,7 @@ void NodeView::ClearGraph()
void NodeView::DeleteSelected()
{
MultiUndoCommand* command = new MultiUndoCommand();
{
// First remove any selected edges
QVector<NodeViewEdge *> selected_edges = scene_.GetSelectedEdges();
if (!selected_edges.isEmpty()) {
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()};
}
}
}
{
// Secondly remove any nodes
QVector<Node*> selected_nodes = scene_.GetSelectedNodes();
// Ensure no nodes are "undeletable"
for (int i=0;i<selected_nodes.size();i++) {
if (!selected_nodes.at(i)->CanBeDeleted()) {
selected_nodes.removeAt(i);
i--;
}
}
if (!selected_nodes.isEmpty()) {
for (Node* node : qAsConst(selected_nodes)) {
command->add_child(new NodeRemoveAndDisconnectCommand(node));
}
}
}
Core::instance()->undo_stack()->pushIfHasChildren(command);
scene_.DeleteSelected();
}
void NodeView::SelectAll()
@@ -428,6 +392,11 @@ void NodeView::mousePressEvent(QMouseEvent *event)
}
super::mousePressEvent(event);
auto selected_items = scene_.GetSelectedItems();
foreach (NodeViewItem *i, selected_items) {
dragging_nodes_.insert(i, i->GetNodePosition());
}
}
void NodeView::mouseMoveEvent(QMouseEvent *event)
@@ -658,6 +627,15 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
DetachItemsFromCursor();
}
for (auto it=dragging_nodes_.cbegin(); it!=dragging_nodes_.cend(); it++) {
NodeViewItem *i = it.key();
QPointF current_pos = i->GetNodePosition();
if (it.value() != current_pos) {
command->add_child(new NodeSetPositionCommand(i->GetNode(), i->GetContext(), current_pos));
}
}
dragging_nodes_.clear();
Core::instance()->undo_stack()->pushIfHasChildren(command);
super::mouseReleaseEvent(event);
+2
View File
@@ -219,6 +219,8 @@ private:
QVector<Node*> last_set_filter_nodes_;
QMap<Node*, QPointF> context_offsets_;
QMap<NodeViewItem*, QPointF> dragging_nodes_;
double scale_;
bool create_edge_already_exists_;
+31 -3
View File
@@ -50,9 +50,7 @@ void NodeViewContext::AddChild(Node *node)
return;
}
NodeViewItem *item = new NodeViewItem(this);
item->SetNode(node, context_);
item->SetNodePosition(context_->GetNodePositionInContext(node));
NodeViewItem *item = new NodeViewItem(node, context_, this);
item->SetFlowDirection(flow_dir_);
connect(node, &Node::InputConnected, this, &NodeViewContext::ChildInputConnected);
@@ -162,6 +160,36 @@ void NodeViewContext::SetCurvedEdges(bool e)
}
}
void NodeViewContext::DeleteSelected(NodeViewDeleteCommand *command)
{
// Delete any selected edges
foreach (NodeViewEdge *edge, edges_) {
if (edge->isSelected()) {
command->AddEdge(edge->output(), edge->input());
}
}
// Delete any selected nodes
foreach (NodeViewItem *node, item_map_) {
if (node->isSelected()) {
command->AddNode(node->GetNode(), context_);
}
}
}
QVector<NodeViewItem *> NodeViewContext::GetSelectedItems() const
{
QVector<NodeViewItem *> items;
for (auto it=item_map_.cbegin(); it!=item_map_.cend(); it++) {
if (it.value()->isSelected()) {
items.append(it.value());
}
}
return items;
}
void NodeViewContext::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
// Set pen and brush
+5
View File
@@ -7,6 +7,7 @@
#include "node/node.h"
#include "nodeviewcommon.h"
#include "nodeviewedge.h"
#include "nodeviewundo.h"
namespace olive {
@@ -22,6 +23,10 @@ public:
void SetCurvedEdges(bool e);
void DeleteSelected(NodeViewDeleteCommand *command);
QVector<NodeViewItem*> GetSelectedItems() const;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
public slots:
+24 -36
View File
@@ -39,10 +39,10 @@
namespace olive {
NodeViewItem::NodeViewItem(QGraphicsItem *parent) :
NodeViewItem::NodeViewItem(Node* n, Node *context, QGraphicsItem *parent) :
QGraphicsRectItem(parent),
node_(nullptr),
context_(nullptr),
node_(n),
context_(context),
expanded_(false),
hide_titlebar_(false),
highlighted_index_(-1),
@@ -69,6 +69,24 @@ NodeViewItem::NodeViewItem(QGraphicsItem *parent) :
setRect(title_bar_rect_);
output_connector_ = new NodeViewItemConnector(true, this);
// Set up node
node_->Retranslate();
foreach (const QString& input, node_->inputs()) {
if (node_->IsInputConnectable(input) && !node_->IsInputHidden(input)) {
node_inputs_.append(input);
}
}
UpdateInputConnectors();
connect(node_, &Node::LabelChanged, this, &NodeViewItem::NodeAppearanceChanged);
connect(node_, &Node::ColorChanged, this, &NodeViewItem::NodeAppearanceChanged);
SetNodePosition(context_->GetNodePositionInContext(node_));
SetExpanded(node_->property("expanded").toBool());
}
QPointF NodeViewItem::GetNodePosition() const
@@ -208,37 +226,6 @@ int NodeViewItem::GetIndexAt(QPointF pt) const
return -1;
}
void NodeViewItem::SetNode(Node *n, Node *context)
{
if (node_) {
disconnect(n, &Node::LabelChanged, this, &NodeViewItem::NodeAppearanceChanged);
disconnect(n, &Node::ColorChanged, this, &NodeViewItem::NodeAppearanceChanged);
}
node_ = n;
context_ = context;
node_inputs_.clear();
input_connectors_.clear();
if (node_) {
node_->Retranslate();
foreach (const QString& input, node_->inputs()) {
if (node_->IsInputConnectable(input) && !node_->IsInputHidden(input)) {
node_inputs_.append(input);
}
}
UpdateInputConnectors();
connect(n, &Node::LabelChanged, this, &NodeViewItem::NodeAppearanceChanged);
connect(n, &Node::ColorChanged, this, &NodeViewItem::NodeAppearanceChanged);
}
update();
}
void NodeViewItem::SetExpanded(bool e, bool hide_titlebar)
{
if (node_inputs_.isEmpty()
@@ -248,6 +235,7 @@ void NodeViewItem::SetExpanded(bool e, bool hide_titlebar)
expanded_ = e;
hide_titlebar_ = hide_titlebar;
node_->setProperty("expanded", e);
if (expanded_ && !node_inputs_.isEmpty()) {
// Create new rect
@@ -507,8 +495,8 @@ void NodeViewItem::SetLabelAsOutput(bool e)
NodeViewEdge *NodeViewItem::GetEdgeFromInputConnector(NodeViewItemConnector *connector)
{
ssize_t index = -1;
for (ssize_t i=0; i<ssize_t(input_connectors_.size()); i++) {
int index = -1;
for (int i=0; i<int(input_connectors_.size()); i++) {
if (input_connectors_[i].get() == connector) {
index = i;
break;
+6 -6
View File
@@ -45,16 +45,11 @@ class NodeViewItem : public QObject, public QGraphicsRectItem
{
Q_OBJECT
public:
NodeViewItem(QGraphicsItem* parent = nullptr);
NodeViewItem(Node* n, Node *context, QGraphicsItem* parent = nullptr);
QPointF GetNodePosition() const;
void SetNodePosition(const QPointF& pos);
/**
* @brief Set the Node to correspond to this widget
*/
void SetNode(Node* n, Node *context);
/**
* @brief Get currently attached node
*/
@@ -63,6 +58,11 @@ public:
return node_;
}
Node *GetContext() const
{
return context_;
}
/**
* @brief Get expanded state
*/
+16 -39
View File
@@ -21,6 +21,7 @@
#include "nodeviewscene.h"
#include "common/functiontimer.h"
#include "core.h"
#include "node/project/sequence/sequence.h"
#include "nodeviewedge.h"
#include "nodeviewitem.h"
@@ -41,11 +42,6 @@ void NodeViewScene::SetFlowDirection(NodeViewCommon::FlowDirection direction)
foreach (NodeViewContext *ctx, context_map_) {
ctx->SetFlowDirection(direction_);
}
// Iterate over edge items setting direction
foreach (NodeViewEdge* edge, edges_) {
edge->SetFlowDirection(direction_);
}
}
void NodeViewScene::clear()
@@ -63,9 +59,6 @@ void NodeViewScene::clear()
delete it.value();
}
item_map_.clear();
qDeleteAll(edges_);
edges_.clear();
}
void NodeViewScene::SelectAll()
@@ -86,22 +79,22 @@ void NodeViewScene::DeselectAll()
}
}
void NodeViewScene::DeleteSelected()
{
NodeViewDeleteCommand* command = new NodeViewDeleteCommand();
foreach (NodeViewContext *ctx, context_map_) {
ctx->DeleteSelected(command);
}
Core::instance()->undo_stack()->push(command);
}
NodeViewItem *NodeViewScene::NodeToUIObject(Node *n)
{
return item_map_.value(n);
}
NodeViewEdge *NodeViewScene::EdgeToUIObject(Node *output, const NodeInput& input)
{
foreach (NodeViewEdge* edge, edges_) {
if (edge->output() == output && edge->input() == input) {
return edge;
}
}
return nullptr;
}
QVector<Node *> NodeViewScene::GetSelectedNodes() const
{
QHash<Node*, NodeViewItem*>::const_iterator iterator;
@@ -118,29 +111,13 @@ QVector<Node *> NodeViewScene::GetSelectedNodes() const
QVector<NodeViewItem *> NodeViewScene::GetSelectedItems() const
{
QHash<Node*, NodeViewItem*>::const_iterator iterator;
QVector<NodeViewItem *> selected;
QVector<NodeViewItem *> items;
for (iterator=item_map_.begin();iterator!=item_map_.end();iterator++) {
if (iterator.value()->isSelected()) {
selected.append(iterator.value());
}
foreach (NodeViewContext *ctx, context_map_) {
items.append(ctx->GetSelectedItems());
}
return selected;
}
QVector<NodeViewEdge *> NodeViewScene::GetSelectedEdges() const
{
QVector<NodeViewEdge*> edges;
foreach (NodeViewEdge* e, edges_) {
if (e->isSelected()) {
edges.append(e);
}
}
return edges;
return items;
}
NodeViewContext *NodeViewScene::AddContext(Node *node)
+2 -9
View File
@@ -42,6 +42,8 @@ public:
void SelectAll();
void DeselectAll();
void DeleteSelected();
/**
* @brief Retrieve the graphical widget corresponding to a specific Node
*
@@ -54,22 +56,15 @@ public:
* in this view/scene), this function returns nullptr.
*/
NodeViewItem* NodeToUIObject(Node* n);
NodeViewEdge *EdgeToUIObject(Node *output, const NodeInput &input);
QVector<Node *> GetSelectedNodes() const;
QVector<NodeViewItem*> GetSelectedItems() const;
QVector<NodeViewEdge*> GetSelectedEdges() const;
const QHash<Node*, NodeViewItem*>& item_map() const
{
return item_map_;
}
const QVector<NodeViewEdge*>& edges() const
{
return edges_;
}
Qt::Orientation GetFlowOrientation() const;
NodeViewCommon::FlowDirection GetFlowDirection() const;
@@ -96,8 +91,6 @@ private:
QHash<Node*, NodeViewItem*> item_map_;
QVector<NodeViewEdge*> edges_;
NodeGraph* graph_;
NodeViewCommon::FlowDirection direction_;
+96
View File
@@ -193,4 +193,100 @@ void NodeOverrideColorCommand::undo()
node_->SetOverrideColor(old_index_);
}
NodeViewDeleteCommand::NodeViewDeleteCommand()
{
}
void NodeViewDeleteCommand::AddNode(Node *node, Node *context)
{
foreach (const NodePair &pair, nodes_) {
if (pair.first == node && pair.second == context) {
return;
}
}
nodes_.append(NodePair({node, context}));
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
if (context->ContextContainsNode(it->second)) {
AddEdge(it->second, it->first);
}
}
for (auto it=node->output_connections().cbegin(); it!=node->output_connections().cend(); it++) {
if (context->ContextContainsNode(it->second.node())) {
AddEdge(it->first, it->second);
}
}
}
void NodeViewDeleteCommand::AddEdge(Node *output, const NodeInput &input)
{
foreach (const Node::OutputConnection &edge, edges_) {
if (edge.first == output && edge.second == input) {
return;
}
}
edges_.append({output, input});
}
Project *NodeViewDeleteCommand::GetRelevantProject() const
{
if (!nodes_.isEmpty()) {
return nodes_.first().first->project();
}
if (!edges_.isEmpty()) {
return edges_.first().first->project();
}
return nullptr;
}
void NodeViewDeleteCommand::redo()
{
foreach (const Node::OutputConnection &edge, edges_) {
Node::DisconnectEdge(edge.first, edge.second);
}
foreach (const NodePair &pair, nodes_) {
RemovedNode rn;
rn.node = pair.first;
rn.context = pair.second;
rn.pos = rn.context->GetNodePositionInContext(rn.node);
rn.context->RemoveNodeFromContext(rn.node);
// If node is no longer in any contexts and is not connected to anything, remove it
if (rn.node->parent()->GetNumberOfContextsNodeIsIn(rn.node, true) == 0
&& rn.node->input_connections().empty()
&& rn.node->output_connections().empty()) {
rn.removed_from_graph = rn.node->parent();
rn.node->setParent(&memory_manager_);
} else {
rn.removed_from_graph = nullptr;
}
removed_nodes_.append(rn);
}
}
void NodeViewDeleteCommand::undo()
{
for (auto rn=removed_nodes_.crbegin(); rn!=removed_nodes_.crend(); rn++) {
if (rn->removed_from_graph) {
rn->node->setParent(rn->removed_from_graph);
}
rn->context->SetNodePositionInContext(rn->node, rn->pos);
}
removed_nodes_.clear();
for (auto edge=edges_.crbegin(); edge!=edges_.crend(); edge++) {
Node::ConnectEdge(edge->first, edge->second);
}
}
}
+36
View File
@@ -363,6 +363,42 @@ private:
};
class NodeViewDeleteCommand : public UndoCommand
{
public:
NodeViewDeleteCommand();
void AddNode(Node *node, Node *context);
void AddEdge(Node *output, const NodeInput &input);
virtual Project * GetRelevantProject() const override;
protected:
virtual void redo() override;
virtual void undo() override;
private:
using NodePair = QPair<Node *, Node*>;
QVector<NodePair> nodes_;
QVector<Node::OutputConnection> edges_;
struct RemovedNode {
Node *node;
Node *context;
QPointF pos;
NodeGraph *removed_from_graph;
};
QVector<RemovedNode> removed_nodes_;
QObject memory_manager_;
};
}
#endif // NODEVIEWUNDO_H