many improvements and reimplementations
This commit is contained in:
@@ -60,6 +60,8 @@ NodeGroupDialog::NodeGroupDialog(NodeGroup *group, QWidget *parent) :
|
||||
QMetaObject::invokeMethod(node_view, &NodeView::CenterOnItemsBoundingRect, Qt::QueuedConnection);
|
||||
splitter->addWidget(node_view);
|
||||
|
||||
splitter->setSizes({splitter->width() / 4, splitter->width() / 4 * 3});
|
||||
|
||||
for (auto it=group->GetInputPassthroughs().cbegin(); it!=group->GetInputPassthroughs().cend(); it++) {
|
||||
param_view->SetInputChecked(it.value(), true);
|
||||
}
|
||||
|
||||
@@ -74,6 +74,12 @@ void NodeGraph::childEvent(QChildEvent *event)
|
||||
connect(node, &Node::ValueChanged, this, &NodeGraph::ValueChanged, Qt::DirectConnection);
|
||||
connect(node, &Node::InputValueHintChanged, this, &NodeGraph::InputValueHintChanged, Qt::DirectConnection);
|
||||
|
||||
if (NodeGroup *group = dynamic_cast<NodeGroup*>(node)) {
|
||||
connect(group, &NodeGroup::InputPassthroughAdded, this, &NodeGraph::GroupAddedInputPassthrough, Qt::DirectConnection);
|
||||
connect(group, &NodeGroup::InputPassthroughRemoved, this, &NodeGraph::GroupRemovedInputPassthrough, Qt::DirectConnection);
|
||||
connect(group, &NodeGroup::OutputPassthroughChanged, this, &NodeGraph::GroupChangedOutputPassthrough, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
emit NodeAdded(node);
|
||||
emit node->AddedToGraph(this);
|
||||
|
||||
@@ -87,12 +93,22 @@ void NodeGraph::childEvent(QChildEvent *event)
|
||||
disconnect(node, &Node::ValueChanged, this, &NodeGraph::ValueChanged);
|
||||
disconnect(node, &Node::InputValueHintChanged, this, &NodeGraph::InputValueHintChanged);
|
||||
|
||||
if (NodeGroup *group = dynamic_cast<NodeGroup*>(node)) {
|
||||
disconnect(group, &NodeGroup::InputPassthroughAdded, this, &NodeGraph::GroupAddedInputPassthrough);
|
||||
disconnect(group, &NodeGroup::InputPassthroughRemoved, this, &NodeGraph::GroupRemovedInputPassthrough);
|
||||
disconnect(group, &NodeGroup::OutputPassthroughChanged, this, &NodeGraph::GroupChangedOutputPassthrough);
|
||||
}
|
||||
|
||||
emit NodeRemoved(node);
|
||||
emit node->RemovedFromGraph(this);
|
||||
|
||||
// Remove from any contexts
|
||||
foreach (Node *context, node_children_) {
|
||||
context->RemoveNodeFromContext(node);
|
||||
|
||||
if (NodeGroup *group = dynamic_cast<NodeGroup*>(context)) {
|
||||
group->RemoveNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -21,6 +21,7 @@
|
||||
#ifndef NODEGRAPH_H
|
||||
#define NODEGRAPH_H
|
||||
|
||||
#include "node/group/group.h"
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -84,9 +85,11 @@ signals:
|
||||
|
||||
void InputValueHintChanged(const NodeInput& input);
|
||||
|
||||
void NodePositionAdded(Node *node, Node *relative, const QPointF &position);
|
||||
void GroupAddedInputPassthrough(NodeGroup *group, const NodeInput &input);
|
||||
|
||||
void NodePositionRemoved(Node *node, Node *relative);
|
||||
void GroupRemovedInputPassthrough(NodeGroup *group, const NodeInput &input);
|
||||
|
||||
void GroupChangedOutputPassthrough(NodeGroup *group, Node *output);
|
||||
|
||||
protected:
|
||||
void AddDefaultNode(Node* n)
|
||||
|
||||
+41
-11
@@ -27,8 +27,6 @@ namespace olive {
|
||||
NodeGroup::NodeGroup() :
|
||||
output_passthrough_(nullptr)
|
||||
{
|
||||
graph_ = new NodeGraph();
|
||||
graph_->setParent(this);
|
||||
}
|
||||
|
||||
QString NodeGroup::Name() const
|
||||
@@ -57,26 +55,28 @@ QString NodeGroup::Description() const
|
||||
|
||||
void NodeGroup::Retranslate()
|
||||
{
|
||||
foreach (Node *n, graph_->nodes()) {
|
||||
foreach (Node *n, nodes_) {
|
||||
n->Retranslate();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeGroup::AddNode(Node *node)
|
||||
{
|
||||
node->setParent(graph_);
|
||||
nodes_.append(node);
|
||||
|
||||
emit NodeAddedToGroup(node);
|
||||
}
|
||||
|
||||
void NodeGroup::RemoveNode(Node *node, QObject *new_parent)
|
||||
void NodeGroup::RemoveNode(Node *node)
|
||||
{
|
||||
if (node->parent() == graph_) {
|
||||
node->setParent(new_parent);
|
||||
if (nodes_.removeOne(node)) {
|
||||
emit NodeRemovedFromGroup(node);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeGroup::AddInputPassthrough(const NodeInput &input)
|
||||
{
|
||||
Q_ASSERT(graph_->nodes().contains(input.node()));
|
||||
Q_ASSERT(nodes_.contains(input.node()));
|
||||
|
||||
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
|
||||
if (it.value() == input) {
|
||||
@@ -91,6 +91,8 @@ void NodeGroup::AddInputPassthrough(const NodeInput &input)
|
||||
AddInput(id, input.GetDataType(), input.GetDefaultValue(), input.GetFlags());
|
||||
|
||||
input_passthroughs_.insert(id, input);
|
||||
|
||||
emit InputPassthroughAdded(this, input);
|
||||
}
|
||||
|
||||
void NodeGroup::RemoveInputPassthrough(const NodeInput &input)
|
||||
@@ -99,6 +101,7 @@ void NodeGroup::RemoveInputPassthrough(const NodeInput &input)
|
||||
if (it.value() == input) {
|
||||
RemoveInput(it.key());
|
||||
input_passthroughs_.erase(it);
|
||||
emit InputPassthroughRemoved(this, it.value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -106,9 +109,11 @@ void NodeGroup::RemoveInputPassthrough(const NodeInput &input)
|
||||
|
||||
void NodeGroup::SetOutputPassthrough(Node *node)
|
||||
{
|
||||
Q_ASSERT(graph_->nodes().contains(node));
|
||||
Q_ASSERT(!node || nodes_.contains(node));
|
||||
|
||||
output_passthrough_ = node;
|
||||
|
||||
emit OutputPassthroughChanged(this, output_passthrough_);
|
||||
}
|
||||
|
||||
QString NodeGroup::GetGroupInputIDFromInput(const NodeInput &input)
|
||||
@@ -135,15 +140,19 @@ bool NodeGroup::ContainsInputPassthrough(const NodeInput &input) const
|
||||
return false;
|
||||
}
|
||||
|
||||
QString NodeGroup::GetInputName(const QString &id) const
|
||||
{
|
||||
return input_passthroughs_.value(id).name();
|
||||
}
|
||||
|
||||
void NodeAddToGroupCommand::redo()
|
||||
{
|
||||
previous_parent_ = node_->parent();
|
||||
group_->AddNode(node_);
|
||||
}
|
||||
|
||||
void NodeAddToGroupCommand::undo()
|
||||
{
|
||||
group_->RemoveNode(node_, previous_parent_);
|
||||
group_->RemoveNode(node_);
|
||||
}
|
||||
|
||||
void NodeGroupSetCustomNameCommand::redo()
|
||||
@@ -174,4 +183,25 @@ void NodeGroupAddInputPassthrough::undo()
|
||||
}
|
||||
}
|
||||
|
||||
void NodeRemoveFromGroupCommand::redo()
|
||||
{
|
||||
group_->RemoveNode(node_);
|
||||
}
|
||||
|
||||
void NodeRemoveFromGroupCommand::undo()
|
||||
{
|
||||
group_->AddNode(node_);
|
||||
}
|
||||
|
||||
void NodeGroupSetOutputPassthrough::redo()
|
||||
{
|
||||
old_output_ = group_->GetOutputPassthrough();
|
||||
group_->SetOutputPassthrough(new_output_);
|
||||
}
|
||||
|
||||
void NodeGroupSetOutputPassthrough::undo()
|
||||
{
|
||||
group_->SetOutputPassthrough(old_output_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+80
-3
@@ -43,12 +43,27 @@ public:
|
||||
|
||||
void AddNode(Node *node);
|
||||
|
||||
void RemoveNode(Node *node, QObject *new_parent = nullptr);
|
||||
void RemoveNode(Node *node);
|
||||
|
||||
bool ContainsNode(Node *node) const
|
||||
{
|
||||
return nodes_.contains(node);
|
||||
}
|
||||
|
||||
const QVector<Node*> &GetNodes() const
|
||||
{
|
||||
return nodes_;
|
||||
}
|
||||
|
||||
void AddInputPassthrough(const NodeInput &input);
|
||||
|
||||
void RemoveInputPassthrough(const NodeInput &input);
|
||||
|
||||
Node *GetOutputPassthrough() const
|
||||
{
|
||||
return output_passthrough_;
|
||||
}
|
||||
|
||||
void SetOutputPassthrough(Node *node);
|
||||
|
||||
const QString &GetCustomName() const
|
||||
@@ -78,8 +93,21 @@ public:
|
||||
|
||||
bool ContainsInputPassthrough(const NodeInput &input) const;
|
||||
|
||||
virtual QString GetInputName(const QString& id) const override;
|
||||
|
||||
signals:
|
||||
void NodeAddedToGroup(Node *node);
|
||||
|
||||
void NodeRemovedFromGroup(Node *node);
|
||||
|
||||
void InputPassthroughAdded(NodeGroup *group, const NodeInput &input);
|
||||
|
||||
void InputPassthroughRemoved(NodeGroup *group, const NodeInput &input);
|
||||
|
||||
void OutputPassthroughChanged(NodeGroup *group, Node *output);
|
||||
|
||||
private:
|
||||
NodeGraph *graph_;
|
||||
QVector<Node*> nodes_;
|
||||
|
||||
QHash<QString, NodeInput> input_passthroughs_;
|
||||
|
||||
@@ -112,7 +140,30 @@ private:
|
||||
|
||||
NodeGroup *group_;
|
||||
|
||||
QObject *previous_parent_;
|
||||
};
|
||||
|
||||
class NodeRemoveFromGroupCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeRemoveFromGroupCommand(Node *node, NodeGroup *group) :
|
||||
node_(node),
|
||||
group_(group)
|
||||
{}
|
||||
|
||||
virtual Project * GetRelevantProject() const override
|
||||
{
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Node *node_;
|
||||
|
||||
NodeGroup *group_;
|
||||
|
||||
};
|
||||
|
||||
@@ -171,6 +222,32 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class NodeGroupSetOutputPassthrough : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeGroupSetOutputPassthrough(NodeGroup *group, Node *output) :
|
||||
group_(group),
|
||||
new_output_(output)
|
||||
{}
|
||||
|
||||
virtual Project * GetRelevantProject() const override
|
||||
{
|
||||
return group_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
NodeGroup *group_;
|
||||
|
||||
Node *new_output_;
|
||||
Node *old_output_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NODEGROUP_H
|
||||
|
||||
+42
-25
@@ -1610,7 +1610,9 @@ void Node::CopyValuesOfElement(const Node *src, Node *dst, const QString &input,
|
||||
dst->SetSplitStandardValue(input, src->GetSplitStandardValue(input, src_element), dst_element);
|
||||
|
||||
// Copy keyframes
|
||||
dst->GetImmediate(input, dst_element)->delete_all_keyframes();
|
||||
if (NodeInputImmediate *immediate = dst->GetImmediate(input, dst_element)) {
|
||||
immediate->delete_all_keyframes();
|
||||
}
|
||||
foreach (const NodeKeyframeTrack& track, src->GetImmediate(input, src_element)->keyframe_tracks()) {
|
||||
foreach (NodeKeyframe* key, track) {
|
||||
key->copy(dst_element, dst);
|
||||
@@ -2366,15 +2368,13 @@ Project *Node::ArrayResizeCommand::GetRelevantProject() const
|
||||
|
||||
void NodeSetPositionCommand::redo()
|
||||
{
|
||||
if (!(added_ = !context_->ContextContainsNode(node_))) {
|
||||
added_ = !context_->ContextContainsNode(node_);
|
||||
|
||||
if (!added_) {
|
||||
old_pos_ = context_->GetNodePositionDataInContext(node_);
|
||||
}
|
||||
|
||||
if (added_) {
|
||||
context_->SetNodePositionInContext(node_, pos_);
|
||||
} else {
|
||||
move(context_, node_, pos_.position - old_pos_.position, move_deps_);
|
||||
}
|
||||
context_->SetNodePositionInContext(node_, pos_);
|
||||
}
|
||||
|
||||
void NodeSetPositionCommand::undo()
|
||||
@@ -2382,23 +2382,7 @@ void NodeSetPositionCommand::undo()
|
||||
if (added_) {
|
||||
context_->RemoveNodeFromContext(node_);
|
||||
} else {
|
||||
move(context_, node_, old_pos_.position - pos_.position, move_deps_);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeSetPositionCommand::move(Node *context, Node *node, const QPointF &diff, bool recursive)
|
||||
{
|
||||
QPointF p = context->GetNodePositionInContext(node);
|
||||
p += diff;
|
||||
context->SetNodePositionInContext(node, p);
|
||||
|
||||
if (recursive) {
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
Node *output = it->second;
|
||||
if (context->ContextContainsNode(output)) {
|
||||
move(context, output, diff, recursive);
|
||||
}
|
||||
}
|
||||
context_->SetNodePositionInContext(node_, old_pos_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2407,7 +2391,7 @@ void NodeRemovePositionFromContextCommand::redo()
|
||||
contained_ = context_->ContextContainsNode(node_);
|
||||
|
||||
if (contained_) {
|
||||
old_pos_ = context_->GetNodePositionInContext(node_);
|
||||
old_pos_ = context_->GetNodePositionDataInContext(node_);
|
||||
context_->RemoveNodeFromContext(node_);
|
||||
}
|
||||
}
|
||||
@@ -2486,4 +2470,37 @@ void Node::ValueHint::Save(QXmlStreamWriter *writer) const
|
||||
writer->writeTextElement(QStringLiteral("tag"), tag_);
|
||||
}
|
||||
|
||||
void NodeSetPositionAndDependenciesRecursivelyCommand::prepare()
|
||||
{
|
||||
move_recursively(node_, pos_.position - context_->GetNodePositionDataInContext(node_).position);
|
||||
}
|
||||
|
||||
void NodeSetPositionAndDependenciesRecursivelyCommand::redo()
|
||||
{
|
||||
for (auto it=commands_.cbegin(); it!=commands_.cend(); it++) {
|
||||
(*it)->redo_now();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeSetPositionAndDependenciesRecursivelyCommand::undo()
|
||||
{
|
||||
for (auto it=commands_.crbegin(); it!=commands_.crend(); it++) {
|
||||
(*it)->undo_now();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeSetPositionAndDependenciesRecursivelyCommand::move_recursively(Node *node, const QPointF &diff)
|
||||
{
|
||||
Node::Position pos = context_->GetNodePositionDataInContext(node);
|
||||
pos += diff;
|
||||
commands_.append(new NodeSetPositionCommand(node_, context_, pos));
|
||||
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
Node *output = it->second;
|
||||
if (context_->ContextContainsNode(output)) {
|
||||
move_recursively(output, diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+33
-7
@@ -320,7 +320,7 @@ public:
|
||||
|
||||
static void DisconnectEdge(Node *output, const NodeInput& input);
|
||||
|
||||
QString GetInputName(const QString& id) const;
|
||||
virtual QString GetInputName(const QString& id) const;
|
||||
|
||||
void LoadInput(QXmlStreamReader* reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled);
|
||||
void SaveInput(QXmlStreamWriter* writer, const QString& id) const;
|
||||
@@ -1420,12 +1420,11 @@ using NodePtr = std::shared_ptr<Node>;
|
||||
class NodeSetPositionCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeSetPositionCommand(Node* node, Node* context, const Node::Position& pos, bool move_dependencies_relatively = false)
|
||||
NodeSetPositionCommand(Node* node, Node* context, const Node::Position& pos)
|
||||
{
|
||||
node_ = node;
|
||||
context_ = context;
|
||||
pos_ = pos;
|
||||
move_deps_ = move_dependencies_relatively;
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
@@ -1439,14 +1438,41 @@ protected:
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
static void move(Node *context, Node *node, const QPointF &diff, bool recursive);
|
||||
|
||||
Node* node_;
|
||||
Node* context_;
|
||||
Node::Position pos_;
|
||||
Node::Position old_pos_;
|
||||
bool added_;
|
||||
bool move_deps_;
|
||||
|
||||
};
|
||||
|
||||
class NodeSetPositionAndDependenciesRecursivelyCommand : public UndoCommand{
|
||||
public:
|
||||
NodeSetPositionAndDependenciesRecursivelyCommand(Node* node, Node* context, const Node::Position& pos) :
|
||||
node_(node),
|
||||
context_(context),
|
||||
pos_(pos)
|
||||
{}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
void move_recursively(Node *node, const QPointF &diff);
|
||||
|
||||
Node* node_;
|
||||
Node* context_;
|
||||
Node::Position pos_;
|
||||
QVector<UndoCommand*> commands_;
|
||||
|
||||
};
|
||||
|
||||
@@ -1474,7 +1500,7 @@ private:
|
||||
|
||||
Node *context_;
|
||||
|
||||
QPointF old_pos_;
|
||||
Node::Position old_pos_;
|
||||
|
||||
bool contained_;
|
||||
|
||||
|
||||
@@ -150,6 +150,15 @@ QVariant NodeInput::GetSplitDefaultValueForTrack(int track) const
|
||||
}
|
||||
}
|
||||
|
||||
int NodeInput::GetArraySize() const
|
||||
{
|
||||
if (IsValid() && element_ == -1) {
|
||||
return node_->InputArraySize(input_);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint qHash(const NodeInput &i)
|
||||
{
|
||||
return qHash(i.node()) ^ qHash(i.input()) ^ qHash(i.element());
|
||||
|
||||
@@ -134,6 +134,16 @@ public:
|
||||
return element_;
|
||||
}
|
||||
|
||||
void set_node(Node *node)
|
||||
{
|
||||
node_ = node;
|
||||
}
|
||||
|
||||
void set_input(const QString &input)
|
||||
{
|
||||
input_ = input;
|
||||
}
|
||||
|
||||
void set_element(int e)
|
||||
{
|
||||
element_ = e;
|
||||
@@ -172,6 +182,8 @@ public:
|
||||
|
||||
QVariant GetSplitDefaultValueForTrack(int track) const;
|
||||
|
||||
int GetArraySize() const;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
*this = NodeInput();
|
||||
|
||||
+160
-108
@@ -49,10 +49,9 @@ NodeView::NodeView(QWidget *parent) :
|
||||
create_edge_(nullptr),
|
||||
create_edge_output_item_(nullptr),
|
||||
create_edge_input_item_(nullptr),
|
||||
create_edge_dst_temp_expanded_(false),
|
||||
create_edge_expand_item_(nullptr),
|
||||
paste_command_(nullptr),
|
||||
scale_(1.0),
|
||||
first_show_(true)
|
||||
scale_(1.0)
|
||||
{
|
||||
setScene(&scene_);
|
||||
SetDefaultDragMode(RubberBandDrag);
|
||||
@@ -311,46 +310,55 @@ void NodeView::mousePressEvent(QMouseEvent *event)
|
||||
QGraphicsItem* item = itemAt(event->pos());
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
// Determine if user clicked on a connector
|
||||
NodeViewItemConnector *connector = dynamic_cast<NodeViewItemConnector *>(item);
|
||||
// Sane defaults
|
||||
create_edge_output_item_ = nullptr;
|
||||
create_edge_input_item_ = nullptr;
|
||||
create_edge_already_exists_ = false;
|
||||
create_edge_from_output_ = true;
|
||||
|
||||
// If the user clicked on a connector OR the user is holding Ctrl
|
||||
if (connector || (event->modifiers() & Qt::ControlModifier)) {
|
||||
// Get the relevant item, either the one attached to the connector or the item if Ctrl+Clicked
|
||||
NodeViewItem *attached_item = connector ? static_cast<NodeViewItem*>(connector->parentItem()) : dynamic_cast<NodeViewItem*>(item);
|
||||
|
||||
if (attached_item) {
|
||||
if (connector && !connector->IsOutput() && (create_edge_ = attached_item->GetEdgeFromInputConnector(connector))) {
|
||||
// Since inputs can only have one edge connected, we grab the existing edge, if one exists
|
||||
create_edge_output_item_ = create_edge_->from_item();
|
||||
create_edge_already_exists_ = true;
|
||||
create_edge_from_output_ = true;
|
||||
} else {
|
||||
// Create a new edge from this output
|
||||
create_edge_ = new NodeViewEdge();
|
||||
create_edge_->SetCurved(scene_.GetEdgesAreCurved());
|
||||
create_edge_->SetFlowDirection(scene_.GetFlowDirection());
|
||||
|
||||
// Set source and declare that we created this edge
|
||||
if ((create_edge_from_output_ = (!connector || connector->IsOutput()))) {
|
||||
// Edge is being created from output
|
||||
create_edge_output_item_ = attached_item;
|
||||
} else {
|
||||
// Edge is being created from input
|
||||
create_edge_input_item_ = attached_item;
|
||||
create_edge_input_ = attached_item->GetInputFromInputConnector(connector);
|
||||
}
|
||||
create_edge_already_exists_ = false;
|
||||
|
||||
// Add edge to scene
|
||||
scene_.addItem(create_edge_);
|
||||
|
||||
// Position edge to mouse cursor
|
||||
PositionNewEdge(event->pos());
|
||||
}
|
||||
return;
|
||||
if (event->modifiers() & Qt::ControlModifier) {
|
||||
create_edge_output_item_ = dynamic_cast<NodeViewItem*>(item);
|
||||
if (create_edge_output_item_ && !create_edge_output_item_->IsOutputItem()) {
|
||||
create_edge_output_item_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!create_edge_output_item_) {
|
||||
// Determine if user clicked on a connector
|
||||
if (NodeViewItemConnector *connector = dynamic_cast<NodeViewItemConnector *>(item)) {
|
||||
NodeViewItem *attached = static_cast<NodeViewItem*>(connector->parentItem());
|
||||
|
||||
if (connector->IsOutput()) {
|
||||
create_edge_output_item_ = attached;
|
||||
} else {
|
||||
create_edge_input_item_ = attached;
|
||||
|
||||
if (!create_edge_input_item_->edges().isEmpty()) {
|
||||
// Drag existing edge instead
|
||||
create_edge_ = create_edge_input_item_->edges().first();
|
||||
create_edge_input_item_ = nullptr;
|
||||
create_edge_output_item_ = create_edge_->from_item();
|
||||
create_edge_already_exists_ = true;
|
||||
} else {
|
||||
create_edge_from_output_ = false;
|
||||
create_edge_input_ = create_edge_input_item_->GetInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((create_edge_output_item_ || create_edge_input_item_) && !create_edge_already_exists_) {
|
||||
// Create a new edge from this output
|
||||
create_edge_ = new NodeViewEdge();
|
||||
create_edge_->SetCurved(scene_.GetEdgesAreCurved());
|
||||
|
||||
// Add edge to scene
|
||||
scene_.addItem(create_edge_);
|
||||
|
||||
// Position edge to mouse cursor
|
||||
PositionNewEdge(event->pos());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle selections with the right mouse button
|
||||
@@ -490,12 +498,12 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
|
||||
|
||||
if (create_edge_output_item_ && create_edge_input_item_) {
|
||||
// Clear highlight if we set one
|
||||
create_edge_input_item_->SetHighlightedIndex(-1);
|
||||
create_edge_input_item_->SetHighlighted(false);
|
||||
|
||||
// Collapse if we expanded it
|
||||
if (create_edge_dst_temp_expanded_) {
|
||||
create_edge_input_item_->SetExpanded(false);
|
||||
create_edge_input_item_->setZValue(0);
|
||||
if (create_edge_expand_item_) {
|
||||
create_edge_expand_item_->SetExpanded(false);
|
||||
create_edge_expand_item_->setZValue(0);
|
||||
}
|
||||
|
||||
NodeInput &creating_input = create_edge_input_;
|
||||
@@ -504,17 +512,20 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
|
||||
if (!reconnected_to_itself) {
|
||||
Node *creating_output = create_edge_output_item_->GetNode();
|
||||
|
||||
if (NodeGroup *output_group = dynamic_cast<NodeGroup*>(creating_output)) {
|
||||
creating_output = output_group->GetOutputPassthrough();
|
||||
}
|
||||
|
||||
if (NodeGroup *input_group = dynamic_cast<NodeGroup*>(creating_input.node())) {
|
||||
creating_input = input_group->GetInputPassthroughs().value(creating_input.input());
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
command->add_child(new NodeEdgeAddCommand(creating_output, creating_input));
|
||||
|
||||
// If the output is not in the input's context, add it now
|
||||
if (!create_edge_input_item_->GetContext()->ContextContainsNode(creating_output)) {
|
||||
command->add_child(new NodeSetPositionCommand(creating_output, create_edge_input_item_->GetContext(), scene_.context_map().value(create_edge_input_item_->GetContext())->MapScenePosToNodePosInContext(create_edge_output_item_->scenePos())));
|
||||
}
|
||||
}
|
||||
|
||||
creating_input.Reset();
|
||||
@@ -686,9 +697,7 @@ void NodeView::ShowContextMenu(const QPoint &pos)
|
||||
|
||||
// Label node action
|
||||
QAction* label_action = m.addAction(tr("Label"));
|
||||
connect(label_action, &QAction::triggered, this, [this](){
|
||||
Core::instance()->LabelNodes(selected_nodes_);
|
||||
});
|
||||
connect(label_action, &QAction::triggered, this, &NodeView::LabelSelectedNodes);
|
||||
|
||||
// Grouping
|
||||
if (selected.size() == 1 && dynamic_cast<NodeGroup*>(selected.first()->GetNode())) {
|
||||
@@ -702,13 +711,19 @@ void NodeView::ShowContextMenu(const QPoint &pos)
|
||||
// Color menu
|
||||
MenuShared::instance()->AddColorCodingMenu(&m);
|
||||
|
||||
ViewerOutput* viewer = dynamic_cast<ViewerOutput*>(selected.first()->GetNode());
|
||||
if (viewer) {
|
||||
// Show in Viewer option for nodes based on Viewer
|
||||
if (ViewerOutput* viewer = dynamic_cast<ViewerOutput*>(selected.first()->GetNode())) {
|
||||
m.addSeparator();
|
||||
QAction* open_in_viewer_action = m.addAction(tr("Open in Viewer"));
|
||||
connect(open_in_viewer_action, &QAction::triggered, this, &NodeView::OpenSelectedNodeInViewer);
|
||||
}
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
// Properties
|
||||
QAction *properties_action = m.addAction(tr("P&roperties"));
|
||||
connect(properties_action, &QAction::triggered, this, &NodeView::ShowNodeProperties);
|
||||
|
||||
} else {
|
||||
|
||||
QAction* curved_action = m.addAction(tr("Smooth Edges"));
|
||||
@@ -988,6 +1003,13 @@ void NodeView::PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNode
|
||||
}*/
|
||||
}
|
||||
|
||||
void NodeView::changeEvent(QEvent *e)
|
||||
{
|
||||
// Add translation code
|
||||
|
||||
super::changeEvent(e);
|
||||
}
|
||||
|
||||
void NodeView::ZoomFromKeyboard(double multiplier)
|
||||
{
|
||||
QPoint cursor_pos = mapFromGlobal(QCursor::pos());
|
||||
@@ -1029,10 +1051,11 @@ void NodeView::PositionNewEdge(const QPoint &pos)
|
||||
item_at_cursor = nullptr;
|
||||
}
|
||||
|
||||
// Filter out connecting to a node that connects to us
|
||||
// Filter out connecting to a node that connects to us or an item of the same type
|
||||
if (item_at_cursor
|
||||
&& ((create_edge_from_output_ && item_at_cursor->GetNode()->OutputsTo(source_item->GetNode(), true))
|
||||
|| (!create_edge_from_output_ && item_at_cursor->GetNode()->InputsFrom(source_item->GetNode(), true)))) {
|
||||
|| (!create_edge_from_output_ && item_at_cursor->GetNode()->InputsFrom(source_item->GetNode(), true))
|
||||
|| (create_edge_from_output_ == item_at_cursor->IsOutputItem()))) {
|
||||
item_at_cursor = nullptr;
|
||||
}
|
||||
|
||||
@@ -1040,46 +1063,32 @@ void NodeView::PositionNewEdge(const QPoint &pos)
|
||||
if (item_at_cursor != opposing_item) {
|
||||
// If we had a destination active, disconnect from it since the item has changed
|
||||
if (opposing_item) {
|
||||
opposing_item->SetHighlightedIndex(-1);
|
||||
opposing_item->SetHighlighted(false);
|
||||
opposing_item = nullptr;
|
||||
}
|
||||
|
||||
if (create_edge_dst_temp_expanded_) {
|
||||
// We expanded this item, so we can un-expand it
|
||||
opposing_item->SetExpanded(false);
|
||||
opposing_item->setZValue(0);
|
||||
// Clear cached input
|
||||
if (create_edge_from_output_) {
|
||||
if (create_edge_input_.IsValid()) {
|
||||
create_edge_input_.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Set destination
|
||||
// If this is an input and we're
|
||||
opposing_item = item_at_cursor;
|
||||
|
||||
// If our destination is an item, ensure it's expanded
|
||||
if (opposing_item) {
|
||||
if (create_edge_from_output_ && (create_edge_dst_temp_expanded_ = (!create_edge_input_item_->IsExpanded()))) {
|
||||
create_edge_input_item_->SetExpanded(true, true);
|
||||
create_edge_input_item_->setZValue(100); // Ensure item is in front
|
||||
opposing_item->SetHighlighted(true);
|
||||
if (!opposing_item->IsOutputItem()) {
|
||||
create_edge_input_ = opposing_item->GetInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a destination, highlight the appropriate input
|
||||
if (create_edge_from_output_) {
|
||||
int highlight_index = -1;
|
||||
if (create_edge_input_item_) {
|
||||
highlight_index = create_edge_input_item_->GetIndexAt(scene_pt);
|
||||
create_edge_input_item_->SetHighlightedIndex(highlight_index);
|
||||
}
|
||||
|
||||
if (highlight_index >= 0) {
|
||||
create_edge_input_ = create_edge_input_item_->GetInputAtIndex(highlight_index);
|
||||
} else {
|
||||
create_edge_input_.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
QPointF output_point = create_edge_output_item_ ? create_edge_output_item_->GetOutputPoint() : scene_pt;
|
||||
QPointF input_point = create_edge_input_.IsValid() ? create_edge_input_item_->GetInputPoint(create_edge_input_.input(), create_edge_input_.element()) : scene_pt;
|
||||
QPointF input_point = create_edge_input_.IsValid() ? create_edge_input_item_->GetInputPoint() : scene_pt;
|
||||
|
||||
create_edge_->SetPoints(output_point, input_point, create_edge_input_item_ && create_edge_input_item_->IsExpanded());
|
||||
create_edge_->SetPoints(output_point, input_point);
|
||||
create_edge_->SetConnected(create_edge_output_item_ && create_edge_input_.IsValid());
|
||||
}
|
||||
|
||||
@@ -1110,36 +1119,14 @@ void NodeView::GroupNodes()
|
||||
// Add group to graph and context
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
|
||||
command->add_child(new NodeAddCommand(context->parent(), group));
|
||||
command->add_child(new NodeSetPositionCommand(group, context, avg_pos));
|
||||
|
||||
// Add nodes to group
|
||||
Node *output_passthrough = nullptr;
|
||||
QVector<Node*> nodes_to_group = selected_nodes_;
|
||||
DeselectAll();
|
||||
foreach (Node *n, nodes_to_group) {
|
||||
for (auto it=n->input_connections().cbegin(); it!=n->input_connections().cend(); it++) {
|
||||
Node *output = it->second;
|
||||
const NodeInput &input = it->first;
|
||||
|
||||
if (!nodes_to_group.contains(output)) {
|
||||
command->add_child(new NodeEdgeRemoveCommand(output, input));
|
||||
command->add_child(new NodeEdgeAddCommand(output, NodeInput(group, input.input(), input.element())));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it=n->output_connections().cbegin(); it!=n->output_connections().cend(); it++) {
|
||||
Node *output = it->first;
|
||||
const NodeInput &input = it->second;
|
||||
|
||||
if (!nodes_to_group.contains(input.node())) {
|
||||
command->add_child(new NodeEdgeRemoveCommand(output, input));
|
||||
command->add_child(new NodeEdgeAddCommand(group, input));
|
||||
}
|
||||
}
|
||||
|
||||
command->add_child(new NodeRemovePositionFromContextCommand(n, context));
|
||||
command->add_child(new NodeAddToGroupCommand(n, group));
|
||||
command->add_child(new NodeSetPositionCommand(n, group, scene_.context_map().value(context)->GetItemFromMap(n)->GetNodePosition()));
|
||||
command->add_child(new NodeSetPositionCommand(n, group, context->GetNodePositionDataInContext(n)));
|
||||
|
||||
for (auto it=n->inputs().cbegin(); it!=n->inputs().cend(); it++) {
|
||||
NodeInput input(n, *it, -1);
|
||||
@@ -1148,8 +1135,25 @@ void NodeView::GroupNodes()
|
||||
command->add_child(new NodeGroupAddInputPassthrough(group, input));
|
||||
}
|
||||
}
|
||||
|
||||
if (!output_passthrough) {
|
||||
// Default to the first node we find that doesn't output to a node inside the group
|
||||
foreach (Node *potential_in, nodes_to_group) {
|
||||
if (potential_in != n && !n->OutputsTo(potential_in, false)) {
|
||||
output_passthrough = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set output passthrough
|
||||
command->add_child(new NodeGroupSetOutputPassthrough(group, output_passthrough));
|
||||
|
||||
// Add group to graph
|
||||
command->add_child(new NodeAddCommand(context->parent(), group));
|
||||
command->add_child(new NodeSetPositionCommand(group, context, avg_pos));
|
||||
|
||||
// Do command
|
||||
command->redo_now();
|
||||
|
||||
@@ -1166,7 +1170,55 @@ void NodeView::GroupNodes()
|
||||
|
||||
void NodeView::UngroupNodes()
|
||||
{
|
||||
//NodeGroup *group = static_cast<NodeGroup*>(selected_nodes_.first());
|
||||
NodeViewItem *group_item = nullptr;
|
||||
QVector<NodeViewItem*> items = scene_.GetSelectedItems();
|
||||
if (items.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
NodeGroup *group;
|
||||
foreach (NodeViewItem *i, items) {
|
||||
if ((group = dynamic_cast<NodeGroup*>(i->GetNode()))) {
|
||||
group_item = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!group_item) {
|
||||
return;
|
||||
}
|
||||
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
|
||||
Node *context = group_item->GetContext();
|
||||
|
||||
command->add_child(new NodeRemovePositionFromContextCommand(group, context));
|
||||
command->add_child(new NodeRemoveAndDisconnectCommand(group));
|
||||
|
||||
foreach (Node *n, group->GetNodes()) {
|
||||
command->add_child(new NodeRemovePositionFromContextCommand(n, group));
|
||||
command->add_child(new NodeRemoveFromGroupCommand(n, group));
|
||||
command->add_child(new NodeSetPositionCommand(n, context, group->GetNodePositionDataInContext(n)));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
}
|
||||
|
||||
void NodeView::ShowNodeProperties()
|
||||
{
|
||||
Node *first_node = selected_nodes_.first();
|
||||
|
||||
if (NodeGroup *group = dynamic_cast<NodeGroup*>(first_node)) {
|
||||
NodeGroupDialog ngd(group, this);
|
||||
ngd.exec();
|
||||
} else {
|
||||
LabelSelectedNodes();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::LabelSelectedNodes()
|
||||
{
|
||||
Core::instance()->LabelNodes(selected_nodes_);
|
||||
}
|
||||
|
||||
void NodeView::PasteNodesInternal(const QVector<Node *> &duplicate_nodes)
|
||||
|
||||
@@ -119,6 +119,8 @@ protected:
|
||||
virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector<Node*> &nodes, void* userdata) override;
|
||||
virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata) override;
|
||||
|
||||
virtual void changeEvent(QEvent *e) override;
|
||||
|
||||
private:
|
||||
void AttachItemsToCursor(const QVector<NodeViewItem *> &items);
|
||||
|
||||
@@ -162,8 +164,8 @@ private:
|
||||
NodeViewEdge* create_edge_;
|
||||
NodeViewItem* create_edge_output_item_;
|
||||
NodeViewItem* create_edge_input_item_;
|
||||
NodeViewItem *create_edge_expand_item_;
|
||||
NodeInput create_edge_input_;
|
||||
bool create_edge_dst_temp_expanded_;
|
||||
bool create_edge_already_exists_;
|
||||
bool create_edge_from_output_;
|
||||
|
||||
@@ -181,8 +183,6 @@ private:
|
||||
|
||||
double scale_;
|
||||
|
||||
bool first_show_;
|
||||
|
||||
static const double kMinimumScale;
|
||||
|
||||
private slots:
|
||||
@@ -225,6 +225,10 @@ private slots:
|
||||
|
||||
void UngroupNodes();
|
||||
|
||||
void ShowNodeProperties();
|
||||
|
||||
void LabelSelectedNodes();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace olive {
|
||||
class NodeViewCommon {
|
||||
public:
|
||||
enum FlowDirection {
|
||||
kInvalidDirection = -1,
|
||||
kTopToBottom,
|
||||
kBottomToTop,
|
||||
kLeftToRight,
|
||||
|
||||
@@ -53,29 +53,16 @@ void NodeViewContext::AddChild(Node *node)
|
||||
NodeViewItem *item = new NodeViewItem(node, context_, this);
|
||||
item->SetFlowDirection(flow_dir_);
|
||||
|
||||
connect(node, &Node::InputConnected, this, &NodeViewContext::ChildInputConnected);
|
||||
connect(node, &Node::InputDisconnected, this, &NodeViewContext::ChildInputDisconnected);
|
||||
AddNodeInternal(node, item);
|
||||
|
||||
item_map_.insert(node, item);
|
||||
|
||||
if (node == context_) {
|
||||
item->SetLabelAsOutput(true);
|
||||
}
|
||||
|
||||
for (auto it=node->output_connections().cbegin(); it!=node->output_connections().cend(); it++) {
|
||||
if (!it->second.IsHidden()) {
|
||||
if (NodeViewItem *other_item = item_map_.value(it->second.node())) {
|
||||
AddEdgeInternal(node, it->second, item, other_item);
|
||||
}
|
||||
if (NodeGroup *group = dynamic_cast<NodeGroup*>(node)) {
|
||||
foreach (Node *n, group->GetNodes()) {
|
||||
// Use this item as the representative for all of these nodes too
|
||||
AddNodeInternal(n, item);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
if (!it->first.IsHidden()) {
|
||||
if (NodeViewItem *other_item = item_map_.value(it->second)) {
|
||||
AddEdgeInternal(it->second, it->first, other_item, item);
|
||||
}
|
||||
}
|
||||
connect(group, &NodeGroup::NodeAddedToGroup, this, &NodeViewContext::GroupAddedNode);
|
||||
connect(group, &NodeGroup::NodeRemovedFromGroup, this, &NodeViewContext::GroupRemovedNode);
|
||||
}
|
||||
|
||||
UpdateRect();
|
||||
@@ -95,12 +82,28 @@ void NodeViewContext::RemoveChild(Node *node)
|
||||
|
||||
// Delete edges first because the edge destructor will try to reference item (maybe that should
|
||||
// be changed...)
|
||||
QVector<NodeViewEdge*> edges_to_remove = item->edges();
|
||||
QVector<NodeViewEdge*> edges_to_remove = item->GetAllEdgesRecursively();
|
||||
foreach (NodeViewEdge *edge, edges_to_remove) {
|
||||
ChildInputDisconnected(edge->output(), edge->input());
|
||||
if (node == item->GetNode() || edge->output() == node || edge->input().node() == node) {
|
||||
ChildInputDisconnected(edge->output(), edge->input());
|
||||
}
|
||||
}
|
||||
|
||||
delete item;
|
||||
// Check if this item is specifically for this node and the node is a group. If so, remove it for
|
||||
// all other entries in the map.
|
||||
if (item->GetNode() == node) {
|
||||
if (dynamic_cast<NodeGroup*>(item->GetNode())) {
|
||||
for (auto it=item_map_.begin(); it!=item_map_.end(); ) {
|
||||
if (it.value() == item) {
|
||||
it = item_map_.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewContext::ChildInputConnected(Node *output, const NodeInput &input)
|
||||
@@ -108,7 +111,7 @@ void NodeViewContext::ChildInputConnected(Node *output, const NodeInput &input)
|
||||
// Add edge
|
||||
if (!input.IsHidden()) {
|
||||
if (NodeViewItem* output_item = item_map_.value(output)) {
|
||||
AddEdgeInternal(output, input, output_item, item_map_.value(input.node()));
|
||||
AddEdgeInternal(output, input, output_item, item_map_.value(input.node())->GetItemForInput(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,8 +120,9 @@ bool NodeViewContext::ChildInputDisconnected(Node *output, const NodeInput &inpu
|
||||
{
|
||||
// Remove edge
|
||||
for (int i=0; i<edges_.size(); i++) {
|
||||
if (edges_.at(i)->output() == output && edges_.at(i)->input() == input) {
|
||||
delete edges_.at(i);
|
||||
NodeViewEdge *e = edges_.at(i);
|
||||
if (e->output() == output && e->input() == input) {
|
||||
delete e;
|
||||
edges_.removeAt(i);
|
||||
return true;
|
||||
}
|
||||
@@ -154,10 +158,6 @@ void NodeViewContext::SetFlowDirection(NodeViewCommon::FlowDirection dir)
|
||||
foreach (NodeViewItem *item, item_map_) {
|
||||
item->SetFlowDirection(dir);
|
||||
}
|
||||
|
||||
foreach (NodeViewEdge *edge, edges_) {
|
||||
edge->SetFlowDirection(dir);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewContext::SetCurvedEdges(bool e)
|
||||
@@ -201,7 +201,9 @@ QVector<NodeViewItem *> NodeViewContext::GetSelectedItems() const
|
||||
|
||||
for (auto it=item_map_.cbegin(); it!=item_map_.cend(); it++) {
|
||||
if (it.value()->isSelected()) {
|
||||
items.append(it.value());
|
||||
if (!items.contains(it.value())) {
|
||||
items.append(it.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,16 +271,62 @@ void NodeViewContext::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
super::mousePressEvent(event);
|
||||
}
|
||||
|
||||
NodeViewEdge* NodeViewContext::AddEdgeInternal(Node *output, const NodeInput& input, NodeViewItem *from, NodeViewItem *to)
|
||||
void NodeViewContext::AddNodeInternal(Node *node, NodeViewItem *item)
|
||||
{
|
||||
connect(node, &Node::InputConnected, this, &NodeViewContext::ChildInputConnected);
|
||||
connect(node, &Node::InputDisconnected, this, &NodeViewContext::ChildInputDisconnected);
|
||||
|
||||
item_map_.insert(node, item);
|
||||
|
||||
if (node == context_) {
|
||||
item->SetLabelAsOutput(true);
|
||||
}
|
||||
|
||||
for (auto it=node->output_connections().cbegin(); it!=node->output_connections().cend(); it++) {
|
||||
if (!it->second.IsHidden()) {
|
||||
if (NodeViewItem *other_item = item_map_.value(it->second.node())) {
|
||||
AddEdgeInternal(node, it->second, item, other_item->GetItemForInput(it->second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
if (!it->first.IsHidden()) {
|
||||
if (NodeViewItem *other_item = item_map_.value(it->second)) {
|
||||
AddEdgeInternal(it->second, it->first, other_item, item->GetItemForInput(it->first));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewContext::AddEdgeInternal(Node *output, const NodeInput& input, NodeViewItem *from, NodeViewItem *to)
|
||||
{
|
||||
if (from == to) {
|
||||
return;
|
||||
}
|
||||
|
||||
NodeViewEdge* edge_ui = new NodeViewEdge(output, input, from, to, this);
|
||||
|
||||
edge_ui->SetFlowDirection(flow_dir_);
|
||||
edge_ui->Adjust();
|
||||
edge_ui->SetCurved(curved_edges_);
|
||||
|
||||
edges_.append(edge_ui);
|
||||
}
|
||||
|
||||
return edge_ui;
|
||||
void NodeViewContext::GroupAddedNode(Node *node)
|
||||
{
|
||||
NodeGroup *group = static_cast<NodeGroup*>(sender());
|
||||
|
||||
AddNodeInternal(node, item_map_.value(group));
|
||||
}
|
||||
|
||||
void NodeViewContext::GroupRemovedNode(Node *node)
|
||||
{
|
||||
NodeGroup *group = static_cast<NodeGroup*>(sender());
|
||||
|
||||
if (item_map_.value(node) == item_map_.value(group)) {
|
||||
item_map_.remove(node);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,7 +60,9 @@ protected:
|
||||
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
NodeViewEdge *AddEdgeInternal(Node *output, const NodeInput& input, NodeViewItem *from, NodeViewItem *to);
|
||||
void AddNodeInternal(Node *node, NodeViewItem *item);
|
||||
|
||||
void AddEdgeInternal(Node *output, const NodeInput& input, NodeViewItem *from, NodeViewItem *to);
|
||||
|
||||
Node *context_;
|
||||
|
||||
@@ -76,6 +78,11 @@ private:
|
||||
|
||||
QVector<NodeViewEdge*> edges_;
|
||||
|
||||
private slots:
|
||||
void GroupAddedNode(Node *node);
|
||||
|
||||
void GroupRemovedNode(Node *node);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -70,12 +70,40 @@ NodeViewEdge::~NodeViewEdge()
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewEdge::set_from_item(NodeViewItem *i)
|
||||
{
|
||||
if (from_item_) {
|
||||
from_item_->RemoveEdge(this);
|
||||
}
|
||||
|
||||
from_item_ = i;
|
||||
|
||||
if (from_item_) {
|
||||
from_item_->AddEdge(this);
|
||||
}
|
||||
|
||||
Adjust();
|
||||
}
|
||||
|
||||
void NodeViewEdge::set_to_item(NodeViewItem *i)
|
||||
{
|
||||
if (to_item_) {
|
||||
to_item_->RemoveEdge(this);
|
||||
}
|
||||
|
||||
to_item_ = i;
|
||||
|
||||
if (to_item_) {
|
||||
to_item_->AddEdge(this);
|
||||
}
|
||||
|
||||
Adjust();
|
||||
}
|
||||
|
||||
void NodeViewEdge::Adjust()
|
||||
{
|
||||
// Draw a line between the two
|
||||
SetPoints(from_item()->GetOutputPoint(),
|
||||
to_item()->GetInputPoint(input_.input(), input_.element()),
|
||||
to_item()->IsExpanded());
|
||||
SetPoints(from_item()->GetOutputPoint(), to_item()->GetInputPoint());
|
||||
}
|
||||
|
||||
void NodeViewEdge::SetConnected(bool c)
|
||||
@@ -92,24 +120,14 @@ void NodeViewEdge::SetHighlighted(bool e)
|
||||
update();
|
||||
}
|
||||
|
||||
void NodeViewEdge::SetPoints(const QPointF &start, const QPointF &end, bool input_is_expanded)
|
||||
void NodeViewEdge::SetPoints(const QPointF &start, const QPointF &end)
|
||||
{
|
||||
cached_start_ = start;
|
||||
cached_end_ = end;
|
||||
cached_input_is_expanded_ = input_is_expanded;
|
||||
|
||||
UpdateCurve();
|
||||
}
|
||||
|
||||
void NodeViewEdge::SetFlowDirection(NodeViewCommon::FlowDirection dir)
|
||||
{
|
||||
flow_dir_ = dir;
|
||||
|
||||
if (from_item_ && to_item_) {
|
||||
Adjust();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewEdge::SetCurved(bool e)
|
||||
{
|
||||
curved_ = e;
|
||||
@@ -146,7 +164,6 @@ void NodeViewEdge::Init()
|
||||
{
|
||||
connected_ = false;
|
||||
highlighted_ = false;
|
||||
flow_dir_ = NodeViewCommon::kLeftToRight;
|
||||
curved_ = true;
|
||||
|
||||
setFlag(QGraphicsItem::ItemIsSelectable);
|
||||
@@ -175,13 +192,26 @@ void NodeViewEdge::UpdateCurve()
|
||||
|
||||
QPointF cp1, cp2;
|
||||
|
||||
if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal) {
|
||||
NodeViewCommon::FlowDirection from_flow = from_item_ ? from_item_->GetFlowDirection() : NodeViewCommon::kInvalidDirection;
|
||||
NodeViewCommon::FlowDirection to_flow = to_item_ ? to_item_->GetFlowDirection() : NodeViewCommon::kInvalidDirection;
|
||||
|
||||
if (from_flow == NodeViewCommon::kInvalidDirection && to_flow == NodeViewCommon::kInvalidDirection) {
|
||||
// This is a technically unsupported scenario, but to avoid issues, we'll use a fallback
|
||||
from_flow = NodeViewCommon::kLeftToRight;
|
||||
to_flow = NodeViewCommon::kLeftToRight;
|
||||
} else if (from_flow == NodeViewCommon::kInvalidDirection) {
|
||||
from_flow = to_flow;
|
||||
} else if (to_flow == NodeViewCommon::kInvalidDirection) {
|
||||
to_flow = from_flow;
|
||||
}
|
||||
|
||||
if (NodeViewCommon::GetFlowOrientation(from_flow) == Qt::Horizontal) {
|
||||
cp1 = QPointF(half_x, start.y());
|
||||
} else {
|
||||
cp1 = QPointF(start.x(), half_y);
|
||||
}
|
||||
|
||||
if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal || cached_input_is_expanded_) {
|
||||
if (NodeViewCommon::GetFlowOrientation(to_flow) == Qt::Horizontal) {
|
||||
cp2 = QPointF(half_x, end.y());
|
||||
} else {
|
||||
cp2 = QPointF(end.x(), half_y);
|
||||
|
||||
@@ -72,6 +72,10 @@ public:
|
||||
return to_item_;
|
||||
}
|
||||
|
||||
void set_from_item(NodeViewItem *i);
|
||||
|
||||
void set_to_item(NodeViewItem *i);
|
||||
|
||||
void Adjust();
|
||||
|
||||
/**
|
||||
@@ -101,12 +105,7 @@ public:
|
||||
/**
|
||||
* @brief Set points to create curve from
|
||||
*/
|
||||
void SetPoints(const QPointF& start, const QPointF& end, bool input_is_expanded);
|
||||
|
||||
/**
|
||||
* @brief Sets the direction nodes are flowing
|
||||
*/
|
||||
void SetFlowDirection(NodeViewCommon::FlowDirection dir);
|
||||
void SetPoints(const QPointF& start, const QPointF& end);
|
||||
|
||||
/**
|
||||
* @brief Set whether edges should be drawn as curved or as straight lines
|
||||
@@ -137,13 +136,10 @@ private:
|
||||
|
||||
bool highlighted_;
|
||||
|
||||
NodeViewCommon::FlowDirection flow_dir_;
|
||||
|
||||
bool curved_;
|
||||
|
||||
QPointF cached_start_;
|
||||
QPointF cached_end_;
|
||||
bool cached_input_is_expanded_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -39,21 +39,17 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeViewItem::NodeViewItem(Node* n, Node *context, QGraphicsItem *parent) :
|
||||
NodeViewItem::NodeViewItem(Node *node, const QString &input, int element, Node *context, QGraphicsItem *parent) :
|
||||
QGraphicsRectItem(parent),
|
||||
node_(n),
|
||||
node_(node),
|
||||
input_(input),
|
||||
element_(element),
|
||||
context_(context),
|
||||
expanded_(false),
|
||||
hide_titlebar_(false),
|
||||
highlighted_index_(-1),
|
||||
flow_dir_(NodeViewCommon::kLeftToRight),
|
||||
highlighted_(false),
|
||||
flow_dir_(NodeViewCommon::kInvalidDirection),
|
||||
label_as_output_(false)
|
||||
{
|
||||
// Set flags for this widget
|
||||
setFlag(QGraphicsItem::ItemIsMovable);
|
||||
setFlag(QGraphicsItem::ItemIsSelectable);
|
||||
setFlag(QGraphicsItem::ItemSendsGeometryChanges);
|
||||
|
||||
//
|
||||
// We use font metrics to set all the UI measurements for DPI-awareness
|
||||
//
|
||||
@@ -61,32 +57,41 @@ NodeViewItem::NodeViewItem(Node* n, Node *context, QGraphicsItem *parent) :
|
||||
// Set border width
|
||||
node_border_width_ = DefaultItemBorder();
|
||||
|
||||
int widget_width = DefaultItemWidth();
|
||||
int widget_height = DefaultItemHeight();
|
||||
|
||||
title_bar_rect_ = QRectF(-widget_width/2, -widget_height/2, widget_width, widget_height);
|
||||
setRect(title_bar_rect_);
|
||||
// Set rect size to default
|
||||
SetRectSize();
|
||||
|
||||
// Create connector
|
||||
input_connector_ = new NodeViewItemConnector(false, this);
|
||||
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);
|
||||
|
||||
if (context_) {
|
||||
SetNodePosition(context_->GetNodePositionInContext(node_));
|
||||
SetExpanded(context_->IsNodeExpandedInContext(node_));
|
||||
if (IsOutputItem()) {
|
||||
connect(node_, &Node::InputAdded, this, &NodeViewItem::RepopulateInputs);
|
||||
connect(node_, &Node::InputRemoved, this, &NodeViewItem::RepopulateInputs);
|
||||
RepopulateInputs();
|
||||
|
||||
// Set flags for this widget
|
||||
setFlag(QGraphicsItem::ItemSendsGeometryChanges);
|
||||
setFlag(QGraphicsItem::ItemIsMovable);
|
||||
setFlag(QGraphicsItem::ItemIsSelectable);
|
||||
|
||||
if (context_) {
|
||||
SetNodePosition(context_->GetNodePositionInContext(node_));
|
||||
SetExpanded(context_->IsNodeExpandedInContext(node_));
|
||||
}
|
||||
} else {
|
||||
output_connector_->setVisible(false);
|
||||
}
|
||||
|
||||
// This should be set during runtime, but just in case here's a default fallback
|
||||
SetFlowDirection(NodeViewCommon::kLeftToRight);
|
||||
}
|
||||
|
||||
NodeViewItem::~NodeViewItem()
|
||||
{
|
||||
Q_ASSERT(edges_.isEmpty());
|
||||
}
|
||||
|
||||
QPointF NodeViewItem::GetNodePosition() const
|
||||
@@ -101,6 +106,17 @@ void NodeViewItem::SetNodePosition(const QPointF &pos)
|
||||
UpdateNodePosition();
|
||||
}
|
||||
|
||||
QVector<NodeViewEdge *> NodeViewItem::GetAllEdgesRecursively() const
|
||||
{
|
||||
QVector<NodeViewEdge *> list = edges_;
|
||||
|
||||
foreach (NodeViewItem *item, children_) {
|
||||
list.append(item->GetAllEdgesRecursively());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
int NodeViewItem::DefaultTextPadding()
|
||||
{
|
||||
return QFontMetrics(QFont()).height() / 4;
|
||||
@@ -139,6 +155,8 @@ QPointF NodeViewItem::NodeToScreenPoint(QPointF p, NodeViewCommon::FlowDirection
|
||||
// Swap X/Y and invert Y
|
||||
p = QPointF(p.y(), -p.x());
|
||||
break;
|
||||
case NodeViewCommon::kInvalidDirection:
|
||||
break;
|
||||
}
|
||||
|
||||
// Multiply by item sizes for this direction
|
||||
@@ -164,12 +182,14 @@ QPointF NodeViewItem::ScreenToNodePoint(QPointF p, NodeViewCommon::FlowDirection
|
||||
break;
|
||||
case NodeViewCommon::kTopToBottom:
|
||||
// Swap X/Y
|
||||
p = QPointF(p.y(), p.x());
|
||||
p = QPointF(p.y(), p.x());
|
||||
break;
|
||||
case NodeViewCommon::kBottomToTop:
|
||||
// Swap X/Y and invert Y
|
||||
p = QPointF(-p.y(), p.x());
|
||||
break;
|
||||
case NodeViewCommon::kInvalidDirection:
|
||||
break;
|
||||
}
|
||||
|
||||
return p;
|
||||
@@ -213,51 +233,79 @@ void NodeViewItem::RemoveEdge(NodeViewEdge *edge)
|
||||
edges_.removeOne(edge);
|
||||
}
|
||||
|
||||
int NodeViewItem::GetIndexAt(QPointF pt) const
|
||||
{
|
||||
pt -= this->scenePos();
|
||||
|
||||
for (int i=0; i<node_inputs_.size(); i++) {
|
||||
if (GetInputRect(i).contains(pt)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void NodeViewItem::SetExpanded(bool e, bool hide_titlebar)
|
||||
{
|
||||
if (node_inputs_.isEmpty()
|
||||
|| (expanded_ == e && hide_titlebar_ == hide_titlebar)) {
|
||||
if ((IsOutputItem() && !has_connectable_inputs_)
|
||||
|| (!IsOutputItem() && !(node_->GetInputFlags(input_) & kInputFlagArray))
|
||||
|| (expanded_ == e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
expanded_ = e;
|
||||
hide_titlebar_ = hide_titlebar;
|
||||
|
||||
if (context_) {
|
||||
context_->SetNodeExpandedInContext(node_, e);
|
||||
}
|
||||
|
||||
if (expanded_ && !node_inputs_.isEmpty()) {
|
||||
// Create new rect
|
||||
QRectF new_rect = title_bar_rect_;
|
||||
|
||||
if (hide_titlebar_) {
|
||||
new_rect.setHeight(new_rect.height() * node_inputs_.size());
|
||||
} else {
|
||||
new_rect.setHeight(new_rect.height() * (node_inputs_.size() + 1));
|
||||
}
|
||||
|
||||
setRect(new_rect);
|
||||
} else {
|
||||
setRect(title_bar_rect_);
|
||||
if (IsOutputItem()) {
|
||||
// We don't have to check has_connectable_inputs_ here because we did it at the top
|
||||
input_connector_->setVisible(!expanded_);
|
||||
}
|
||||
|
||||
UpdateInputConnectorFlowDirections();
|
||||
if (expanded_) {
|
||||
if (IsOutputItem()) {
|
||||
// Create items for each input of the node
|
||||
int i = 1;
|
||||
foreach (const QString &input, node_->inputs()) {
|
||||
if (IsInputValid(input)) {
|
||||
NodeViewItem *item = new NodeViewItem(node_, input, -1, context_, this);
|
||||
item->setPos(QPointF(0, i * item->rect().height()));
|
||||
children_.append(item);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateConnectorPositions();
|
||||
QVector<NodeViewEdge*> edges = edges_;
|
||||
for (auto it=edges.cbegin(); it!=edges.cend(); it++) {
|
||||
if ((*it)->to_item() == this) {
|
||||
(*it)->set_to_item(GetItemForInput((*it)->input()));
|
||||
}
|
||||
}
|
||||
|
||||
SetRectSize(i);
|
||||
} else {
|
||||
// Create items for each element of the input array
|
||||
int arr_sz = node_->InputArraySize(input_);
|
||||
children_.resize(arr_sz);
|
||||
for (int i=0; i<arr_sz; i++) {
|
||||
NodeViewItem *item = new NodeViewItem(node_, input_, i, context_, this);
|
||||
item->setPos(pos() + QPointF(0, (i+1) * item->rect().height()));
|
||||
children_[i] = item;
|
||||
}
|
||||
|
||||
QVector<NodeViewEdge*> edges = edges_;
|
||||
for (auto it=edges.cbegin(); it!=edges.cend(); it++) {
|
||||
if ((*it)->to_item() == this) {
|
||||
(*it)->set_to_item(GetItemForInput((*it)->input()));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach (NodeViewItem *child, children_) {
|
||||
QVector<NodeViewEdge*> child_edges = child->edges();
|
||||
foreach (NodeViewEdge *edge, child_edges) {
|
||||
edge->set_to_item(this);
|
||||
}
|
||||
delete child;
|
||||
}
|
||||
children_.clear();
|
||||
|
||||
SetRectSize(1);
|
||||
}
|
||||
|
||||
if (flow_dir_ == NodeViewCommon::kTopToBottom) {
|
||||
UpdateOutputConnectorPosition();
|
||||
}
|
||||
|
||||
ReadjustAllEdges();
|
||||
|
||||
@@ -277,35 +325,16 @@ void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
|
||||
// has been slightly modified
|
||||
QPalette app_pal = Core::instance()->main_window()->palette();
|
||||
|
||||
// Draw background rect if expanded
|
||||
if (IsExpanded()) {
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->setBrush(app_pal.color(QPalette::Window));
|
||||
|
||||
painter->drawRect(rect());
|
||||
|
||||
painter->setPen(app_pal.color(QPalette::Text));
|
||||
|
||||
for (int i=0;i<node_inputs_.size();i++) {
|
||||
QRectF input_rect = GetInputRect(i);
|
||||
|
||||
if (highlighted_index_ == i) {
|
||||
QColor highlight_col = app_pal.color(QPalette::Text);
|
||||
highlight_col.setAlpha(64);
|
||||
painter->fillRect(input_rect, highlight_col);
|
||||
}
|
||||
|
||||
painter->drawText(input_rect, Qt::AlignCenter, node_->GetInputName(node_inputs_.at(i)));
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the titlebar
|
||||
if (!hide_titlebar_ && node_) {
|
||||
if (IsOutputItem()) {
|
||||
QRectF single_unit_rect = rect();
|
||||
single_unit_rect.setHeight(DefaultItemHeight());
|
||||
|
||||
// Output item drawing code
|
||||
painter->setPen(Qt::black);
|
||||
painter->setBrush(node_->brush(title_bar_rect_.top(), title_bar_rect_.bottom()));
|
||||
painter->setBrush(node_->brush(single_unit_rect.top(), single_unit_rect.bottom()));
|
||||
|
||||
painter->drawRect(title_bar_rect_);
|
||||
painter->drawRect(single_unit_rect);
|
||||
|
||||
painter->setPen(app_pal.color(QPalette::Text));
|
||||
|
||||
@@ -320,40 +349,54 @@ void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
|
||||
|
||||
int icon_size = painter->fontMetrics().height()/2;
|
||||
|
||||
bool draw_arrow = !node_inputs_.isEmpty();
|
||||
|
||||
if (node_label.isEmpty()) {
|
||||
// Draw shortname only
|
||||
DrawNodeTitle(painter, node_shortname, title_bar_rect_, Qt::AlignVCenter, icon_size, draw_arrow);
|
||||
DrawNodeTitle(painter, node_shortname, single_unit_rect, Qt::AlignVCenter, icon_size, has_connectable_inputs_);
|
||||
} else {
|
||||
int text_pad = DefaultTextPadding()/2;
|
||||
QRectF safe_label_bounds = title_bar_rect_.adjusted(text_pad, text_pad, -text_pad, -text_pad);
|
||||
QRectF safe_label_bounds = single_unit_rect.adjusted(text_pad, text_pad, -text_pad, -text_pad);
|
||||
QFont f;
|
||||
qreal font_sz = f.pointSizeF();
|
||||
f.setPointSizeF(font_sz * 0.8);
|
||||
painter->setFont(f);
|
||||
DrawNodeTitle(painter, node_label, safe_label_bounds, Qt::AlignTop, icon_size, draw_arrow);
|
||||
DrawNodeTitle(painter, node_label, safe_label_bounds, Qt::AlignTop, icon_size, has_connectable_inputs_);
|
||||
f.setPointSizeF(font_sz * 0.6);
|
||||
painter->setFont(f);
|
||||
DrawNodeTitle(painter, node_shortname, safe_label_bounds, Qt::AlignBottom, icon_size, false);
|
||||
}
|
||||
|
||||
}
|
||||
// Draw final border
|
||||
QPen border_pen;
|
||||
border_pen.setWidth(node_border_width_);
|
||||
|
||||
// Draw final border
|
||||
QPen border_pen;
|
||||
border_pen.setWidth(node_border_width_);
|
||||
if (option->state & QStyle::State_Selected) {
|
||||
border_pen.setColor(app_pal.color(QPalette::Highlight));
|
||||
} else {
|
||||
border_pen.setColor(Qt::black);
|
||||
}
|
||||
|
||||
if (option->state & QStyle::State_Selected) {
|
||||
border_pen.setColor(app_pal.color(QPalette::Highlight));
|
||||
painter->setPen(border_pen);
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
|
||||
painter->drawRect(rect());
|
||||
} else {
|
||||
border_pen.setColor(Qt::black);
|
||||
// Input item drawing code
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->setBrush(app_pal.color(QPalette::Window));
|
||||
|
||||
painter->drawRect(rect());
|
||||
|
||||
if (highlighted_) {
|
||||
QColor highlight_col = app_pal.color(QPalette::Text);
|
||||
highlight_col.setAlpha(64);
|
||||
painter->setBrush(highlight_col);
|
||||
painter->drawRect(rect());
|
||||
}
|
||||
|
||||
painter->setPen(app_pal.color(QPalette::Text));
|
||||
|
||||
painter->drawText(rect(), Qt::AlignCenter, node_->GetInputName(input_));
|
||||
}
|
||||
|
||||
painter->setPen(border_pen);
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
|
||||
painter->drawRect(rect());
|
||||
}
|
||||
|
||||
void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
@@ -399,11 +442,16 @@ QVariant NodeViewItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons
|
||||
|
||||
void NodeViewItem::ReadjustAllEdges()
|
||||
{
|
||||
UpdateInputConnectorFlowDirections();
|
||||
UpdateInputConnectorPositions();
|
||||
foreach (NodeViewEdge* edge, edges_) {
|
||||
if (NodeViewItem *to_item = edge->to_item()) {
|
||||
static_cast<NodeViewItem*>(to_item->parentItem())->UpdateFlowDirectionOfInputItem(to_item);
|
||||
}
|
||||
|
||||
edge->Adjust();
|
||||
}
|
||||
foreach (NodeViewItem *child, children_) {
|
||||
child->ReadjustAllEdges();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewItem::UpdateContextRect()
|
||||
@@ -420,19 +468,19 @@ void NodeViewItem::DrawNodeTitle(QPainter* painter, QString text, const QRectF&
|
||||
painter->setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
|
||||
// Draw right or down arrow based on expanded state
|
||||
int icon_padding = title_bar_rect_.height() / 2 - icon_size / 2;
|
||||
int icon_padding = DefaultItemHeight() / 2 - icon_size / 2;
|
||||
int icon_full_size = icon_size + icon_padding * 2;
|
||||
if (draw_arrow) {
|
||||
const QIcon& expand_icon = IsExpanded() ? icon::TriDown : icon::TriRight;
|
||||
int icon_size_scaled = icon_size * painter->transform().m11();
|
||||
painter->drawPixmap(QRect(title_bar_rect_.x() + icon_padding,
|
||||
title_bar_rect_.y() + icon_padding,
|
||||
painter->drawPixmap(QRect(this->rect().x() + icon_padding,
|
||||
this->rect().y() + icon_padding,
|
||||
icon_size,
|
||||
icon_size), expand_icon.pixmap(QSize(icon_size_scaled, icon_size_scaled)));
|
||||
}
|
||||
|
||||
// Calculate how much space we have for text
|
||||
int item_width = title_bar_rect_.width();
|
||||
int item_width = this->rect().width();
|
||||
int max_text_width = item_width - DefaultTextPadding() * 2 - icon_full_size;
|
||||
int label_width = QtUtils::QFontMetricsWidth(fm, text);
|
||||
|
||||
@@ -467,28 +515,6 @@ void NodeViewItem::DrawNodeTitle(QPainter* painter, QString text, const QRectF&
|
||||
text);
|
||||
}
|
||||
|
||||
NodeViewEdge *NodeViewItem::GetEdgeFromInputIndex(int index)
|
||||
{
|
||||
foreach (NodeViewEdge *edge, edges_) {
|
||||
if (edge->input().input() == node_inputs_.at(index)) {
|
||||
return edge;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NodeViewItem::SetHighlightedIndex(int index)
|
||||
{
|
||||
if (highlighted_index_ == index) {
|
||||
return;
|
||||
}
|
||||
|
||||
highlighted_index_ = index;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void NodeViewItem::SetLabelAsOutput(bool e)
|
||||
{
|
||||
label_as_output_ = e;
|
||||
@@ -496,58 +522,9 @@ void NodeViewItem::SetLabelAsOutput(bool e)
|
||||
update();
|
||||
}
|
||||
|
||||
NodeInput NodeViewItem::GetInputFromInputConnector(NodeViewItemConnector *connector)
|
||||
QPointF NodeViewItem::GetInputPoint() const
|
||||
{
|
||||
for (int i=0; i<int(input_connectors_.size()); i++) {
|
||||
if (input_connectors_[i].get() == connector) {
|
||||
return NodeInput(node_, node_inputs_.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
return NodeInput();
|
||||
}
|
||||
|
||||
NodeViewEdge *NodeViewItem::GetEdgeFromInputConnector(NodeViewItemConnector *connector)
|
||||
{
|
||||
int index = -1;
|
||||
for (int i=0; i<int(input_connectors_.size()); i++) {
|
||||
if (input_connectors_[i].get() == connector) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == -1) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return GetEdgeFromInputIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
QRectF NodeViewItem::GetInputRect(int index) const
|
||||
{
|
||||
QRectF r = title_bar_rect_;
|
||||
|
||||
if (!hide_titlebar_) {
|
||||
index++;
|
||||
}
|
||||
|
||||
if (IsExpanded()) {
|
||||
r.translate(0, r.height() * index);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
QPointF NodeViewItem::GetInputPoint(const QString &input, int element) const
|
||||
{
|
||||
int index = node_inputs_.indexOf(input);
|
||||
|
||||
if (index < 0 || index >= int(input_connectors_.size())) {
|
||||
return pos();
|
||||
}
|
||||
|
||||
return input_connectors_[index]->scenePos();
|
||||
return input_connector_->scenePos();
|
||||
}
|
||||
|
||||
QPointF NodeViewItem::GetOutputPoint() const
|
||||
@@ -576,13 +553,21 @@ QPointF NodeViewItem::GetOutputPoint() const
|
||||
|
||||
void NodeViewItem::SetFlowDirection(NodeViewCommon::FlowDirection dir)
|
||||
{
|
||||
flow_dir_ = dir;
|
||||
if (flow_dir_ != dir) {
|
||||
flow_dir_ = dir;
|
||||
|
||||
UpdateInputConnectorFlowDirections();
|
||||
output_connector_->SetFlowDirection(dir);
|
||||
input_connector_->SetFlowDirection(dir);
|
||||
output_connector_->SetFlowDirection(dir);
|
||||
|
||||
UpdateConnectorPositions();
|
||||
UpdateNodePosition();
|
||||
UpdateInputConnectorPosition();
|
||||
UpdateOutputConnectorPosition();
|
||||
|
||||
if (IsOutputItem()) {
|
||||
UpdateNodePosition();
|
||||
}
|
||||
|
||||
ReadjustAllEdges();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewItem::UpdateNodePosition()
|
||||
@@ -590,26 +575,47 @@ void NodeViewItem::UpdateNodePosition()
|
||||
setPos(NodeToScreenPoint(cached_node_pos_, flow_dir_));
|
||||
}
|
||||
|
||||
void NodeViewItem::UpdateInputConnectors()
|
||||
void NodeViewItem::UpdateInputConnectorPosition()
|
||||
{
|
||||
int old_sz = input_connectors_.size();
|
||||
QRectF output_rect = input_connector_->boundingRect();
|
||||
|
||||
input_connectors_.resize(node_inputs_.size());
|
||||
for (size_t i=old_sz; i<input_connectors_.size(); i++) {
|
||||
input_connectors_[i] = std::make_unique<NodeViewItemConnector>(false, this);
|
||||
NodeViewCommon::FlowDirection using_flow_dir = flow_dir_;
|
||||
|
||||
if (IsExpanded() && !NodeViewCommon::IsFlowHorizontal(flow_dir_)) {
|
||||
if (edges_.isEmpty() || edges_.first()->from_item()->x() < this->x()) {
|
||||
using_flow_dir = NodeViewCommon::kLeftToRight;
|
||||
} else {
|
||||
using_flow_dir = NodeViewCommon::kRightToLeft;
|
||||
}
|
||||
}
|
||||
|
||||
// Input connector flow directions change conditionally
|
||||
switch (using_flow_dir) {
|
||||
case NodeViewCommon::kLeftToRight:
|
||||
input_connector_->setPos(rect().left() - output_rect.width(), 0);
|
||||
break;
|
||||
case NodeViewCommon::kRightToLeft:
|
||||
input_connector_->setPos(rect().right() + output_rect.width(), 0);
|
||||
break;
|
||||
case NodeViewCommon::kTopToBottom:
|
||||
input_connector_->setPos(rect().center().x(), rect().top() - output_rect.height());
|
||||
break;
|
||||
case NodeViewCommon::kBottomToTop:
|
||||
input_connector_->setPos(rect().center().x(), rect().bottom() + output_rect.height());
|
||||
break;
|
||||
case NodeViewCommon::kInvalidDirection:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewItem::UpdateConnectorPositions()
|
||||
void NodeViewItem::UpdateOutputConnectorPosition()
|
||||
{
|
||||
UpdateInputConnectorPositions();
|
||||
|
||||
switch (flow_dir_) {
|
||||
case NodeViewCommon::kLeftToRight:
|
||||
output_connector_->setPos(rect().right(), title_bar_rect_.center().y());
|
||||
output_connector_->setPos(rect().right(), 0);
|
||||
break;
|
||||
case NodeViewCommon::kRightToLeft:
|
||||
output_connector_->setPos(rect().left(), title_bar_rect_.center().y());
|
||||
output_connector_->setPos(rect().left(), 0);
|
||||
break;
|
||||
case NodeViewCommon::kTopToBottom:
|
||||
output_connector_->setPos(rect().center().x(), rect().bottom());
|
||||
@@ -617,57 +623,94 @@ void NodeViewItem::UpdateConnectorPositions()
|
||||
case NodeViewCommon::kBottomToTop:
|
||||
output_connector_->setPos(rect().center().x(), rect().top());
|
||||
break;
|
||||
case NodeViewCommon::kInvalidDirection:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewItem::UpdateInputConnectorPositions()
|
||||
bool NodeViewItem::IsInputValid(const QString &input)
|
||||
{
|
||||
QRectF output_rect = output_connector_->boundingRect();
|
||||
|
||||
// Input connector flow directions change conditionally
|
||||
for (size_t i=0; i<input_connectors_.size(); i++) {
|
||||
switch (GetFlowDirectionForInput(i)) {
|
||||
case NodeViewCommon::kLeftToRight:
|
||||
input_connectors_[i]->setPos(rect().left() - output_rect.width(), GetInputRect(i).center().y());
|
||||
break;
|
||||
case NodeViewCommon::kRightToLeft:
|
||||
input_connectors_[i]->setPos(rect().right() + output_rect.width(), GetInputRect(i).center().y());
|
||||
break;
|
||||
case NodeViewCommon::kTopToBottom:
|
||||
input_connectors_[i]->setPos(rect().center().x(), rect().top() - output_rect.height());
|
||||
break;
|
||||
case NodeViewCommon::kBottomToTop:
|
||||
input_connectors_[i]->setPos(rect().center().x(), rect().bottom() + output_rect.height());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return node_->IsInputConnectable(input) && !node_->IsInputHidden(input);
|
||||
}
|
||||
|
||||
void NodeViewItem::UpdateInputConnectorFlowDirections()
|
||||
void NodeViewItem::SetRectSize(int height_units)
|
||||
{
|
||||
for (size_t i=0; i<input_connectors_.size(); i++) {
|
||||
input_connectors_[i]->SetFlowDirection(GetFlowDirectionForInput(i));
|
||||
}
|
||||
// Set rect
|
||||
int widget_width = DefaultItemWidth();
|
||||
int widget_height = DefaultItemHeight();
|
||||
|
||||
setRect(QRectF(-widget_width/2, -widget_height/2, widget_width, widget_height * height_units));
|
||||
}
|
||||
|
||||
NodeViewCommon::FlowDirection NodeViewItem::GetFlowDirectionForInput(int index)
|
||||
void NodeViewItem::UpdateFlowDirectionOfInputItem(NodeViewItem *child)
|
||||
{
|
||||
if (!expanded_ || NodeViewCommon::IsFlowHorizontal(flow_dir_)) {
|
||||
return flow_dir_;
|
||||
} else {
|
||||
NodeViewEdge *edge = GetEdgeFromInputIndex(index);
|
||||
|
||||
if (!edge || edge->from_item()->x() < this->x()) {
|
||||
return NodeViewCommon::kLeftToRight;
|
||||
if (!child->IsOutputItem()) {
|
||||
if (NodeViewCommon::IsFlowVertical(flow_dir_)) {
|
||||
if (!child->edges().isEmpty() && child->edges().first()->from_item()->scenePos().x() > child->scenePos().x()) {
|
||||
child->SetFlowDirection(NodeViewCommon::kRightToLeft);
|
||||
} else {
|
||||
child->SetFlowDirection(NodeViewCommon::kLeftToRight);
|
||||
}
|
||||
} else {
|
||||
return NodeViewCommon::kRightToLeft;
|
||||
child->SetFlowDirection(flow_dir_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewItem::RepopulateInputs()
|
||||
{
|
||||
has_connectable_inputs_ = false;
|
||||
|
||||
foreach (const QString& input, node_->inputs()) {
|
||||
if (IsInputValid(input)) {
|
||||
has_connectable_inputs_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
input_connector_->setVisible(has_connectable_inputs_);
|
||||
}
|
||||
|
||||
void NodeViewItem::NodeAppearanceChanged()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
void NodeViewItem::SetHighlighted(bool e)
|
||||
{
|
||||
highlighted_ = e;
|
||||
update();
|
||||
}
|
||||
|
||||
NodeViewItem *NodeViewItem::GetItemForInput(NodeInput input)
|
||||
{
|
||||
if (NodeGroup *group = dynamic_cast<NodeGroup*>(node_)) {
|
||||
if (input.node() != group) {
|
||||
// Translate input to group input
|
||||
QString id = NodeGroup::GetGroupInputIDFromInput(input);
|
||||
input.set_node(group);
|
||||
input.set_input(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsExpanded()) {
|
||||
if (input_.isEmpty()) {
|
||||
// Look for the input in our children
|
||||
foreach (NodeViewItem *i, children_) {
|
||||
if (i->input_ == input.input()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Look for element in our children
|
||||
if (input.element() >= 0 && input.element() < children_.size()) {
|
||||
return children_.at(input.element());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to this object
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
class NodeViewItem;
|
||||
class NodeViewEdge;
|
||||
|
||||
/**
|
||||
@@ -45,11 +46,19 @@ class NodeViewItem : public QObject, public QGraphicsRectItem
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeViewItem(Node* n, Node *context, QGraphicsItem* parent = nullptr);
|
||||
NodeViewItem(Node *node, const QString &input, int element, Node *context, QGraphicsItem* parent = nullptr);
|
||||
NodeViewItem(Node *node, Node *context, QGraphicsItem* parent = nullptr) :
|
||||
NodeViewItem(node, QString(), -1, context, parent)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~NodeViewItem() override;
|
||||
|
||||
QPointF GetNodePosition() const;
|
||||
void SetNodePosition(const QPointF& pos);
|
||||
|
||||
QVector<NodeViewEdge*> GetAllEdgesRecursively() const;
|
||||
|
||||
/**
|
||||
* @brief Get currently attached node
|
||||
*/
|
||||
@@ -58,6 +67,11 @@ public:
|
||||
return node_;
|
||||
}
|
||||
|
||||
NodeInput GetInput() const
|
||||
{
|
||||
return NodeInput(node_, input_, element_);
|
||||
}
|
||||
|
||||
Node *GetContext() const
|
||||
{
|
||||
return context_;
|
||||
@@ -82,11 +96,7 @@ public:
|
||||
void SetExpanded(bool e, bool hide_titlebar = false);
|
||||
void ToggleExpanded();
|
||||
|
||||
/**
|
||||
* @brief Returns GLOBAL point that edges should connect to for any NodeParam member of this object
|
||||
*/
|
||||
QPointF GetInputPoint(const QString& input, int element) const;
|
||||
|
||||
QPointF GetInputPoint() const;
|
||||
QPointF GetOutputPoint() const;
|
||||
|
||||
/**
|
||||
@@ -94,6 +104,11 @@ public:
|
||||
*/
|
||||
void SetFlowDirection(NodeViewCommon::FlowDirection dir);
|
||||
|
||||
NodeViewCommon::FlowDirection GetFlowDirection() const
|
||||
{
|
||||
return flow_dir_;
|
||||
}
|
||||
|
||||
static int DefaultTextPadding();
|
||||
|
||||
static int DefaultItemHeight();
|
||||
@@ -113,19 +128,20 @@ public:
|
||||
void AddEdge(NodeViewEdge* edge);
|
||||
void RemoveEdge(NodeViewEdge* edge);
|
||||
|
||||
int GetIndexAt(QPointF pt) const;
|
||||
|
||||
NodeInput GetInputAtIndex(int index) const
|
||||
{
|
||||
return NodeInput(node_, node_inputs_.at(index));
|
||||
}
|
||||
|
||||
void SetHighlightedIndex(int index);
|
||||
|
||||
void SetLabelAsOutput(bool e);
|
||||
|
||||
NodeInput GetInputFromInputConnector(NodeViewItemConnector *connector);
|
||||
NodeViewEdge *GetEdgeFromInputConnector(NodeViewItemConnector *connector);
|
||||
void SetHighlighted(bool e);
|
||||
|
||||
NodeViewItem *GetItemForInput(NodeInput input);
|
||||
|
||||
bool IsOutputItem() const
|
||||
{
|
||||
return input_.isEmpty();
|
||||
}
|
||||
|
||||
void ReadjustAllEdges();
|
||||
|
||||
void UpdateFlowDirectionOfInputItem(NodeViewItem *child);
|
||||
|
||||
protected:
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
||||
@@ -138,49 +154,35 @@ protected:
|
||||
virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
|
||||
|
||||
private:
|
||||
void ReadjustAllEdges();
|
||||
|
||||
void UpdateContextRect();
|
||||
|
||||
void DrawNodeTitle(QPainter *painter, QString text, const QRectF &rect, Qt::Alignment vertical_align, int icon_size, bool draw_arrow);
|
||||
|
||||
NodeViewEdge *GetEdgeFromInputIndex(int index);
|
||||
|
||||
/**
|
||||
* @brief Returns local rect of a NodeInput in array node_inputs_[index]
|
||||
*/
|
||||
QRectF GetInputRect(int index) const;
|
||||
|
||||
/**
|
||||
* @brief Internal update function when logical position changes
|
||||
*/
|
||||
void UpdateNodePosition();
|
||||
|
||||
void UpdateInputConnectors();
|
||||
void UpdateInputConnectorPosition();
|
||||
void UpdateOutputConnectorPosition();
|
||||
|
||||
void UpdateConnectorPositions();
|
||||
bool IsInputValid(const QString &input);
|
||||
|
||||
void UpdateInputConnectorPositions();
|
||||
void UpdateInputConnectorFlowDirections();
|
||||
|
||||
NodeViewCommon::FlowDirection GetFlowDirectionForInput(int index);
|
||||
void SetRectSize(int height_units = 1);
|
||||
|
||||
/**
|
||||
* @brief Reference to attached Node
|
||||
*/
|
||||
Node* node_;
|
||||
Node *node_;
|
||||
QString input_;
|
||||
int element_;
|
||||
|
||||
Node *context_;
|
||||
|
||||
/**
|
||||
* @brief Cached list of node inputs
|
||||
*/
|
||||
QVector<QString> node_inputs_;
|
||||
|
||||
/**
|
||||
* @brief Rectangle of the Node's title bar (equal to rect() when collapsed)
|
||||
*/
|
||||
QRectF title_bar_rect_;
|
||||
QVector<NodeViewItem*> children_;
|
||||
|
||||
/// Sizing variables to use when drawing
|
||||
int node_border_width_;
|
||||
@@ -190,9 +192,7 @@ private:
|
||||
*/
|
||||
bool expanded_;
|
||||
|
||||
bool hide_titlebar_;
|
||||
|
||||
int highlighted_index_;
|
||||
bool highlighted_;
|
||||
|
||||
NodeViewCommon::FlowDirection flow_dir_;
|
||||
|
||||
@@ -200,14 +200,18 @@ private:
|
||||
|
||||
QPointF cached_node_pos_;
|
||||
|
||||
std::vector<std::unique_ptr<NodeViewItemConnector> > input_connectors_;
|
||||
NodeViewItemConnector *input_connector_;
|
||||
NodeViewItemConnector *output_connector_;
|
||||
|
||||
bool has_connectable_inputs_;
|
||||
|
||||
bool label_as_output_;
|
||||
|
||||
private slots:
|
||||
void NodeAppearanceChanged();
|
||||
|
||||
void RepopulateInputs();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -74,6 +74,8 @@ void NodeViewItemConnector::SetFlowDirection(NodeViewCommon::FlowDirection dir)
|
||||
p[1] = QPointF(-triangle_sz_half, 0);
|
||||
p[2] = QPointF(0, triangle_sz_half);
|
||||
break;
|
||||
case NodeViewCommon::kInvalidDirection:
|
||||
break;
|
||||
}
|
||||
|
||||
setPolygon(p);
|
||||
|
||||
@@ -153,7 +153,7 @@ void TimelineAddTrackCommand::redo()
|
||||
if (create_pos_command) {
|
||||
position_command_->add_child(new NodeSetPositionCommand(track_, sequence, sequence->GetNodePositionInContext(sequence) + QPointF(-1, -position_factor)));
|
||||
position_command_->add_child(new NodeSetPositionCommand(merge_, sequence, sequence->GetNodePositionInContext(sequence)));
|
||||
position_command_->add_child(new NodeSetPositionCommand(merge_, sequence, sequence->GetNodePositionInContext(sequence) + QPointF(-1, position_factor * timeline_->GetTrackCount()), true));
|
||||
position_command_->add_child(new NodeSetPositionAndDependenciesRecursivelyCommand(merge_, sequence, sequence->GetNodePositionInContext(sequence) + QPointF(-1, position_factor * timeline_->GetTrackCount())));
|
||||
}
|
||||
} else if (direct_.IsValid() && !direct_.IsConnected()) {
|
||||
// If no merge, we have a direct connection, and nothing else is connected, connect this
|
||||
|
||||
@@ -750,7 +750,7 @@ void MainWindow::SetDefaultLayout()
|
||||
|
||||
node_panel_->show();
|
||||
tabifyDockWidget(param_panel_, node_panel_);
|
||||
footage_viewer_panel_->raise();
|
||||
param_panel_->raise();
|
||||
|
||||
curve_panel_->hide();
|
||||
curve_panel_->setFloating(true);
|
||||
|
||||
Reference in New Issue
Block a user