many improvements and reimplementations

This commit is contained in:
itsmattkc
2021-11-29 21:22:49 -08:00
parent 495b07fc55
commit 239a019690
21 changed files with 874 additions and 495 deletions
+2
View File
@@ -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);
}
+16
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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_;
+9
View File
@@ -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());
+12
View File
@@ -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
View File
@@ -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)
+7 -3
View File
@@ -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();
};
}
+1
View File
@@ -30,6 +30,7 @@ namespace olive {
class NodeViewCommon {
public:
enum FlowDirection {
kInvalidDirection = -1,
kTopToBottom,
kBottomToTop,
kLeftToRight,
+82 -34
View File
@@ -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);
}
}
}
+8 -1
View File
@@ -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);
};
}
+47 -17
View File
@@ -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);
+5 -9
View File
@@ -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_;
};
+273 -230
View File
@@ -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;
}
}
+47 -43
View File
@@ -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
+1 -1
View File
@@ -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);