Merge branch 'node-view-show-ctx'

This commit is contained in:
itsmattkc
2022-01-02 12:15:55 -08:00
152 changed files with 7409 additions and 5465 deletions
+1
View File
@@ -20,6 +20,7 @@ add_subdirectory(color)
add_subdirectory(distort)
add_subdirectory(filter)
add_subdirectory(generator)
add_subdirectory(group)
add_subdirectory(input)
add_subdirectory(math)
add_subdirectory(output)
+2
View File
@@ -47,6 +47,8 @@ Block::Block() :
IgnoreHashingFrom(kLengthInput);
AddInput(kEnabledInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
SetFlags(kDontShowInParamView);
}
QVector<Node::CategoryID> Block::Category() const
+1 -3
View File
@@ -233,13 +233,11 @@ void CropDistortNode::GizmoMove(const QPointF &p, const rational &time, const Qt
}
}
void CropDistortNode::GizmoRelease()
void CropDistortNode::GizmoRelease(MultiUndoCommand *command)
{
MultiUndoCommand *command = new MultiUndoCommand();
for (NodeInputDragger& i : gizmo_dragger_) {
i.End(command);
}
Core::instance()->undo_stack()->push(command);
gizmo_dragger_.clear();
gizmo_start_.clear();
+1 -1
View File
@@ -76,7 +76,7 @@ public:
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF &p) override;
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
virtual void GizmoRelease() override;
virtual void GizmoRelease(MultiUndoCommand *command) override;
static const QString kTextureInput;
static const QString kLeftInput;
@@ -301,13 +301,11 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time, con
}
}
void TransformDistortNode::GizmoRelease()
void TransformDistortNode::GizmoRelease(MultiUndoCommand *command)
{
MultiUndoCommand *command = new MultiUndoCommand();
for (NodeInputDragger& i : gizmo_dragger_) {
i.End(command);
}
Core::instance()->undo_stack()->push(command);
gizmo_dragger_.clear();
gizmo_start_.clear();
@@ -78,7 +78,7 @@ public:
virtual bool GizmoPress(const NodeValueRow &row, const NodeGlobals &globals, const QPointF &p) override;
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
virtual void GizmoRelease() override;
virtual void GizmoRelease(MultiUndoCommand *command) override;
enum AutoScaleType {
kAutoScaleNone,
+11
View File
@@ -54,6 +54,7 @@
namespace olive {
QList<Node*> NodeFactory::library_;
QVector<int> NodeFactory::hidden_;
void NodeFactory::Initialize()
{
@@ -65,6 +66,9 @@ void NodeFactory::Initialize()
library_.append(created_node);
}
hidden_.append(kTextGeneratorLegacy);
hidden_.append(kGroupNode);
}
void NodeFactory::Destroy()
@@ -86,6 +90,11 @@ Menu *NodeFactory::CreateMenu(QWidget* parent, bool create_none_item, Node::Cate
continue;
}
if (hidden_.contains(i)) {
// Skip this node
continue;
}
// Make sure nodes are up-to-date with the current translation
n->Retranslate();
@@ -241,6 +250,8 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
return new SubtitleBlock();
case kShapeGenerator:
return new ShapeNode();
case kGroupNode:
return new NodeGroup();
case kInternalNodeCount:
break;
+3
View File
@@ -61,6 +61,7 @@ public:
kTimeRemapNode,
kSubtitleBlock,
kShapeGenerator,
kGroupNode,
// Count value
kInternalNodeCount
@@ -87,6 +88,8 @@ public:
private:
static QList<Node*> library_;
static QVector<int> hidden_;
};
}
+1 -3
View File
@@ -297,13 +297,11 @@ void ShapeNodeBase::GizmoMove(const QPointF &p, const rational &time, const Qt::
}
}
void ShapeNodeBase::GizmoRelease()
void ShapeNodeBase::GizmoRelease(MultiUndoCommand *command)
{
MultiUndoCommand *command = new MultiUndoCommand();
for (NodeInputDragger& i : gizmo_dragger_) {
i.End(command);
}
Core::instance()->undo_stack()->push(command);
gizmo_dragger_.clear();
}
+1 -1
View File
@@ -49,7 +49,7 @@ public:
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF &p) override;
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
virtual void GizmoRelease() override;
virtual void GizmoRelease(MultiUndoCommand *command) override;
private:
static QVector2D GenerateGizmoAnchor(const QVector2D &pos, const QVector2D &size, int drag, QVector2D *pt);
+18 -40
View File
@@ -44,26 +44,12 @@ void NodeGraph::Clear()
}
}
qreal NodeGraph::GetNodeContextHeight(Node *context)
{
const PositionMap &map = position_map_.value(context);
qreal top = 0, bottom = 0;
foreach (const QPointF &pt, map) {
top = qMin(pt.y(), top);
bottom = qMax(pt.y(), bottom);
}
return bottom - top;
}
int NodeGraph::GetNumberOfContextsNodeIsIn(Node *node) const
int NodeGraph::GetNumberOfContextsNodeIsIn(Node *node, bool except_itself) const
{
int count = 0;
for (auto it=position_map_.cbegin(); it!=position_map_.cend(); it++) {
if (it.value().contains(node)) {
foreach (Node *ctx, node_children_) {
if (ctx->ContextContainsNode(node) && (!except_itself || ctx != node)) {
count++;
}
}
@@ -71,18 +57,6 @@ int NodeGraph::GetNumberOfContextsNodeIsIn(Node *node) const
return count;
}
bool NodeGraph::NodeOutputsToContext(Node *node) const
{
for (auto it=position_map_.cbegin(); it!=position_map_.cend(); it++) {
const PositionMap &pm = it.value();
if (pm.contains(node) && node->OutputsTo(it.key(), true)) {
return true;
}
}
return false;
}
void NodeGraph::childEvent(QChildEvent *event)
{
super::childEvent(event);
@@ -100,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);
@@ -113,21 +93,19 @@ 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);
for (auto it=position_map_.begin(); it!=position_map_.end(); it++) {
PositionMap &map = it.value();
for (auto jt=map.begin(); jt!=map.end(); ) {
if (jt.key() == node) {
jt = map.erase(jt);
emit NodePositionRemoved(node, it.key());
} else {
jt++;
}
}
// Remove from any contexts
foreach (Node *context, node_children_) {
context->RemoveNodeFromContext(node);
}
}
}
}
+6 -54
View File
@@ -21,6 +21,7 @@
#ifndef NODEGRAPH_H
#define NODEGRAPH_H
#include "node/group/group.h"
#include "node/node.h"
namespace olive {
@@ -63,54 +64,7 @@ public:
return default_nodes_;
}
bool NodeMapContainsNode(Node* node, Node* context) const
{
return position_map_.value(context).contains(node);
}
QPointF GetNodePosition(Node* node, Node* context)
{
return position_map_.value(context).value(node);
}
void SetNodePosition(Node* node, Node* context, const QPointF& pos)
{
position_map_[context].insert(node, pos);
emit NodePositionAdded(node, context, pos);
}
void RemoveNodePosition(Node* node, Node* context)
{
PositionMap& map = position_map_[context];
map.remove(node);
if (map.isEmpty()) {
position_map_.remove(context);
}
emit NodePositionRemoved(node, context);
}
bool ContextContainsNode(Node *node, Node *context)
{
return position_map_[context].contains(node);
}
qreal GetNodeContextHeight(Node *context);
using PositionMap = QHash<Node*, QPointF>;
const PositionMap &GetNodesForContext(Node *context)
{
return position_map_[context];
}
const QMap<Node *, PositionMap> &GetPositionMap() const
{
return position_map_;
}
int GetNumberOfContextsNodeIsIn(Node *node) const;
bool NodeOutputsToContext(Node *node) const;
int GetNumberOfContextsNodeIsIn(Node *node, bool except_itself = false) const;
signals:
/**
@@ -131,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)
@@ -148,10 +104,6 @@ private:
QVector<Node*> default_nodes_;
QMap<Node *, PositionMap> position_map_;
PositionMap root_position_map_;
};
}
+22
View File
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2021 Olive Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
node/group/group.cpp
node/group/group.h
PARENT_SCOPE
)
+215
View File
@@ -0,0 +1,215 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "group.h"
#include "node/graph.h"
namespace olive {
#define super Node
NodeGroup::NodeGroup() :
output_passthrough_(nullptr)
{
}
QString NodeGroup::Name() const
{
return tr("Group");
}
QString NodeGroup::id() const
{
return QStringLiteral("org.olivevideoeditor.Olive.group");
}
QVector<Node::CategoryID> NodeGroup::Category() const
{
return {kCategoryGeneral};
}
QString NodeGroup::Description() const
{
return tr("A group of nodes that is represented as a single node.");
}
void NodeGroup::Retranslate()
{
for (auto it=GetContextPositions().cbegin(); it!=GetContextPositions().cend(); it++) {
it.key()->Retranslate();
}
}
void NodeGroup::AddInputPassthrough(const NodeInput &input)
{
Q_ASSERT(ContextContainsNode(input.node()));
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
if (it.value() == input) {
// Already passing this input through
return;
}
}
// Add input
QString id = GetGroupInputIDFromInput(input);
AddInput(id, input.GetDataType(), input.GetDefaultValue(), input.GetFlags());
input_passthroughs_.insert(id, input);
emit InputPassthroughAdded(this, input);
}
void NodeGroup::RemoveInputPassthrough(const NodeInput &input)
{
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
if (it.value() == input) {
RemoveInput(it.key());
input_passthroughs_.erase(it);
emit InputPassthroughRemoved(this, it.value());
break;
}
}
}
void NodeGroup::SetOutputPassthrough(Node *node)
{
Q_ASSERT(!node || ContextContainsNode(node));
output_passthrough_ = node;
emit OutputPassthroughChanged(this, output_passthrough_);
}
QString NodeGroup::GetGroupInputIDFromInput(const NodeInput &input)
{
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(input.node()->GetUUID().toByteArray());
hash.addData(input.input().toUtf8());
hash.addData((const char*) &input.element(), sizeof(input.element()));
return QString::fromLatin1(hash.result().toHex());
}
bool NodeGroup::ContainsInputPassthrough(const NodeInput &input) const
{
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
if (it.value() == input) {
return true;
}
}
return false;
}
QString NodeGroup::GetInputName(const QString &id) const
{
return input_passthroughs_.value(id).name();
}
bool NodeGroup::LoadCustom(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt *cancelled)
{
if (reader->name() == QStringLiteral("inputpassthroughs")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("inputpassthrough")) {
XMLNodeData::GroupLink link;
link.group = this;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
link.input_node = reader->readElementText().toULongLong();
} else if (reader->name() == QStringLiteral("input")) {
link.input_id = reader->readElementText();
} else if (reader->name() == QStringLiteral("element")) {
link.input_element = reader->readElementText().toInt();
} else {
reader->skipCurrentElement();
}
}
xml_node_data.group_input_links.append(link);
} else {
reader->skipCurrentElement();
}
}
return true;
} else if (reader->name() == QStringLiteral("outputpassthrough")) {
xml_node_data.group_output_links.insert(this, reader->readElementText().toULongLong());
return true;
} else {
return super::LoadCustom(reader, xml_node_data, version, cancelled);
}
}
void NodeGroup::SaveCustom(QXmlStreamWriter *writer) const
{
super::SaveCustom(writer);
writer->writeStartElement(QStringLiteral("inputpassthroughs"));
foreach (const NodeInput &ip, input_passthroughs_) {
writer->writeStartElement(QStringLiteral("inputpassthrough"));
writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast<quintptr>(ip.node())));
writer->writeTextElement(QStringLiteral("input"), ip.input());
writer->writeTextElement(QStringLiteral("element"), QString::number(ip.element()));
writer->writeEndElement(); // input
}
writer->writeEndElement(); // inputpassthroughs
writer->writeTextElement(QStringLiteral("outputpassthrough"), QString::number(reinterpret_cast<quintptr>(output_passthrough_)));
}
void NodeGroupAddInputPassthrough::redo()
{
if (!group_->ContainsInputPassthrough(input_)) {
group_->AddInputPassthrough(input_);
actually_added_ = true;
} else {
actually_added_ = false;
}
}
void NodeGroupAddInputPassthrough::undo()
{
if (actually_added_) {
group_->RemoveInputPassthrough(input_);
}
}
void NodeGroupSetOutputPassthrough::redo()
{
old_output_ = group_->GetOutputPassthrough();
group_->SetOutputPassthrough(new_output_);
}
void NodeGroupSetOutputPassthrough::undo()
{
group_->SetOutputPassthrough(old_output_);
}
}
+141
View File
@@ -0,0 +1,141 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef NODEGROUP_H
#define NODEGROUP_H
#include "node/node.h"
namespace olive {
class NodeGroup : public Node
{
Q_OBJECT
public:
NodeGroup();
NODE_DEFAULT_DESTRUCTOR(NodeGroup)
NODE_COPY_FUNCTION(NodeGroup)
virtual QString Name() const override;
virtual QString id() const override;
virtual QVector<CategoryID> Category() const override;
virtual QString Description() const override;
virtual void Retranslate() override;
void AddInputPassthrough(const NodeInput &input);
void RemoveInputPassthrough(const NodeInput &input);
Node *GetOutputPassthrough() const
{
return output_passthrough_;
}
void SetOutputPassthrough(Node *node);
static QString GetGroupInputIDFromInput(const NodeInput &input);
const QHash<QString, NodeInput> &GetInputPassthroughs() const
{
return input_passthroughs_;
}
bool ContainsInputPassthrough(const NodeInput &input) const;
virtual QString GetInputName(const QString& id) const override;
signals:
void InputPassthroughAdded(NodeGroup *group, const NodeInput &input);
void InputPassthroughRemoved(NodeGroup *group, const NodeInput &input);
void OutputPassthroughChanged(NodeGroup *group, Node *output);
protected:
virtual bool LoadCustom(QXmlStreamReader* reader, XMLNodeData& xml_node_data, uint version, const QAtomicInt* cancelled) override;
virtual void SaveCustom(QXmlStreamWriter* writer) const override;
private:
QHash<QString, NodeInput> input_passthroughs_;
Node *output_passthrough_;
};
class NodeGroupAddInputPassthrough : public UndoCommand
{
public:
NodeGroupAddInputPassthrough(NodeGroup *group, const NodeInput &input) :
group_(group),
input_(input),
actually_added_(false)
{}
virtual Project * GetRelevantProject() const override
{
return group_->project();
}
protected:
virtual void redo() override;
virtual void undo() override;
private:
NodeGroup *group_;
NodeInput input_;
bool actually_added_;
};
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
+1
View File
@@ -43,6 +43,7 @@ public:
* @brief Methods of interpolation to use with this keyframe
*/
enum Type {
kInvalid = -1,
kLinear,
kHold,
kBezier
+2
View File
@@ -32,6 +32,8 @@ MergeNode::MergeNode()
AddInput(kBaseIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
AddInput(kBlendIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
SetFlags(kDontShowInParamView);
}
Node *MergeNode::copy() const
+130 -130
View File
@@ -47,8 +47,10 @@ Node::Node() :
override_color_(-1),
folder_(nullptr),
operation_stack_(0),
cache_result_(false)
cache_result_(false),
flags_(kNone)
{
uuid_ = QUuid::createUuid();
}
Node::~Node()
@@ -88,6 +90,8 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint versi
xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), this);
} else if (reader->name() == QStringLiteral("label")) {
SetLabel(reader->readElementText());
} else if (reader->name() == QStringLiteral("uuid")) {
SetUUID(QUuid::fromString(reader->readElementText()));
} else if (reader->name() == QStringLiteral("color")) {
override_color_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("links")) {
@@ -169,6 +173,7 @@ void Node::Save(QXmlStreamWriter *writer) const
{
writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
writer->writeTextElement(QStringLiteral("uuid"), uuid_.toString());
writer->writeTextElement(QStringLiteral("label"), GetLabel());
writer->writeTextElement(QStringLiteral("color"), QString::number(override_color_));
@@ -219,7 +224,16 @@ void Node::Save(QXmlStreamWriter *writer) const
Project* Node::project() const
{
return dynamic_cast<Project*>(parent());
QObject *t = this->parent();
while (t) {
if (Project *p = dynamic_cast<Project*>(t)) {
return p;
}
t = t->parent();
}
return nullptr;
}
QString Node::ShortName() const
@@ -243,6 +257,40 @@ QIcon Node::icon() const
return icon::New;
}
bool Node::SetNodePositionInContext(Node *node, const QPointF &pos)
{
Position p = context_positions_.value(node);
p.position = pos;
return SetNodePositionInContext(node, p);
}
bool Node::SetNodePositionInContext(Node *node, const Position &pos)
{
bool added = !ContextContainsNode(node);
context_positions_.insert(node, pos);
if (added) {
emit NodeAddedToContext(node);
}
emit NodePositionInContextChanged(node, pos.position);
return added;
}
bool Node::RemoveNodeFromContext(Node *node)
{
if (ContextContainsNode(node)) {
context_positions_.remove(node);
emit NodeRemovedFromContext(node);
return true;
} else {
return false;
}
}
Color Node::color() const
{
int c;
@@ -429,6 +477,11 @@ void Node::SaveInput(QXmlStreamWriter *writer, const QString &id) const
writer->writeEndElement(); // subelements
}
bool Node::IsInputHidden(const QString &input) const
{
return (GetInputFlags(input) & kInputFlagHidden);
}
bool Node::IsInputConnectable(const QString &input) const
{
return !(GetInputFlags(input) & kInputFlagNotConnectable);
@@ -1050,7 +1103,7 @@ NodeInputImmediate *Node::GetImmediate(const QString &input, int element) const
return nullptr;
}
Node::InputFlags Node::GetInputFlags(const QString &input) const
InputFlags Node::GetInputFlags(const QString &input) const
{
const Input* i = GetInternalInputData(input);
@@ -1196,13 +1249,10 @@ Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap<Node*, Node*>& cre
command->add_child(new NodeSetValueHintCommand(copied_input, node->GetValueHintForInput(input.input(), input.element())));
}
if (node->parent()->GetPositionMap().contains(node)) {
// This node is a context, copy the context
const NodeGraph::PositionMap &map = node->parent()->GetPositionMap().value(node);
for (auto it=map.cbegin(); it!=map.cend(); it++) {
// Add either the copy (if it exists) or the original node to the context
command->add_child(new NodeSetPositionCommand(created.value(it.key(), it.key()), copy, it.value(), false));
}
const PositionMap &map = node->GetContextPositions();
for (auto it=map.cbegin(); it!=map.cend(); it++) {
// Add either the copy (if it exists) or the original node to the context
command->add_child(new NodeSetPositionCommand(created.value(it.key(), it.key()), copy, it.value()));
}
return copy;
@@ -1229,13 +1279,10 @@ Node *Node::CopyNodeInGraph(Node *node, MultiUndoCommand *command)
command->add_child(new NodeCopyInputsCommand(node, copy, true));
if (node->parent()->GetPositionMap().contains(node)) {
// This node is a context, copy the context
const NodeGraph::PositionMap &map = node->parent()->GetPositionMap().value(node);
for (auto it=map.cbegin(); it!=map.cend(); it++) {
// Add to the context
command->add_child(new NodeSetPositionCommand(it.key(), copy, it.value(), false));
}
const PositionMap &map = node->GetContextPositions();
for (auto it=map.cbegin(); it!=map.cend(); it++) {
// Add to the context
command->add_child(new NodeSetPositionCommand(it.key(), copy, it.value()));
}
}
@@ -1310,7 +1357,7 @@ void Node::HashAddNodeSignature(QCryptographicHash &hash) const
hash.addData(id().toUtf8());
}
void Node::InsertInput(const QString &id, NodeValue::Type type, const QVariant &default_value, Node::InputFlags flags, int index)
void Node::InsertInput(const QString &id, NodeValue::Type type, const QVariant &default_value, InputFlags flags, int index)
{
if (id.isEmpty()) {
qWarning() << "Rejected adding input with an empty ID on node" << this->id();
@@ -1457,7 +1504,7 @@ void Node::GizmoMove(const QPointF &, const rational&, const Qt::KeyboardModifie
{
}
void Node::GizmoRelease()
void Node::GizmoRelease(MultiUndoCommand *)
{
}
@@ -1564,7 +1611,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);
@@ -2198,7 +2247,6 @@ void Node::childEvent(QChildEvent *event)
GetImmediate(key->input(), key->element())->insert_keyframe(key);
connect(key, &NodeKeyframe::TimeChanged, this, &Node::InvalidateFromKeyframeTimeChange);
connect(key, &NodeKeyframe::TimeChanged, this, &Node::KeyframeTimeChanged);
connect(key, &NodeKeyframe::ValueChanged, this, &Node::InvalidateFromKeyframeValueChange);
connect(key, &NodeKeyframe::TypeChanged, this, &Node::InvalidateFromKeyframeTypeChanged);
connect(key, &NodeKeyframe::BezierControlInChanged, this, &Node::InvalidateFromKeyframeBezierInChange);
@@ -2210,15 +2258,14 @@ void Node::childEvent(QChildEvent *event)
TimeRange time_affected = GetRangeAffectedByKeyframe(key);
disconnect(key, &NodeKeyframe::TimeChanged, this, &Node::InvalidateFromKeyframeTimeChange);
disconnect(key, &NodeKeyframe::TimeChanged, this, &Node::KeyframeTimeChanged);
disconnect(key, &NodeKeyframe::ValueChanged, this, &Node::InvalidateFromKeyframeValueChange);
disconnect(key, &NodeKeyframe::TypeChanged, this, &Node::InvalidateFromKeyframeTypeChanged);
disconnect(key, &NodeKeyframe::BezierControlInChanged, this, &Node::InvalidateFromKeyframeBezierInChange);
disconnect(key, &NodeKeyframe::BezierControlOutChanged, this, &Node::InvalidateFromKeyframeBezierOutChange);
GetImmediate(key->input(), key->element())->remove_keyframe(key);
emit KeyframeRemoved(key);
GetImmediate(key->input(), key->element())->remove_keyframe(key);
ParameterValueChanged(i, time_affected);
}
}
@@ -2281,12 +2328,16 @@ void Node::InvalidateFromKeyframeTimeChange()
foreach (const TimeRange& r, invalidate_range) {
ParameterValueChanged(key->key_track_ref().input(), r);
}
emit KeyframeTimeChanged(key);
}
void Node::InvalidateFromKeyframeValueChange()
{
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
ParameterValueChanged(key->key_track_ref().input(), GetRangeAffectedByKeyframe(key));
emit KeyframeValueChanged(key);
}
void Node::InvalidateFromKeyframeTypeChanged()
@@ -2301,6 +2352,8 @@ void Node::InvalidateFromKeyframeTypeChanged()
// Invalidate entire range
ParameterValueChanged(key->key_track_ref().input(), GetRangeAroundIndex(key->input(), track.indexOf(key), key->track(), key->element()));
emit KeyframeTypeChanged(key);
}
Project *Node::ArrayInsertCommand::GetRelevantProject() const
@@ -2318,119 +2371,40 @@ Project *Node::ArrayResizeCommand::GetRelevantProject() const
return node_->project();
}
void NodeSetPositionAndShiftSurroundingsCommand::redo()
{
if (commands_.isEmpty()) {
// Move first node
NodeSetPositionCommand* set_pos_command = new NodeSetPositionCommand(node_, relative_, position_, move_dependencies_);
set_pos_command->redo_now();
commands_.append(set_pos_command);
// Get bounding rect
qreal bounding_rect_sz = 1.0;
qreal bounding_rect_half_sz = bounding_rect_sz * 0.5;
QRectF bounding_rect(position_.x() - bounding_rect_half_sz, position_.y() - bounding_rect_half_sz, bounding_rect_sz, bounding_rect_sz);
// Start moving other nodes
foreach (Node* surrounding, node_->parent()->nodes()) {
if (surrounding != node_) {
QPointF surrounding_position = node_->parent()->GetNodePosition(surrounding, relative_);
if (bounding_rect.contains(surrounding_position)) {
QPointF new_pos = surrounding_position;
qreal move_rate = 0.50;
if (surrounding_position.y() < position_.y()) {
move_rate = -move_rate;
}
new_pos.setY(new_pos.y() + move_rate);
auto sur_command = new NodeSetPositionAndShiftSurroundingsCommand(surrounding, relative_, new_pos, true);
sur_command->redo();
commands_.append(sur_command);
}
}
}
} else {
for (int i=0; i<commands_.size(); i++) {
commands_.at(i)->redo_now();
}
}
}
void NodeSetPositionCommand::redo()
{
graph_ = node_->parent();
if (!(added_ = !graph_->NodeMapContainsNode(node_, relevant_))) {
old_pos_ = graph_->GetNodePosition(node_, relevant_);
added_ = !context_->ContextContainsNode(node_);
if (!added_) {
old_pos_ = context_->GetNodePositionDataInContext(node_);
}
graph_->SetNodePosition(node_, relevant_, pos_);
context_->SetNodePositionInContext(node_, pos_);
}
void NodeSetPositionCommand::undo()
{
if (added_) {
graph_->RemoveNodePosition(node_, relevant_);
context_->RemoveNodeFromContext(node_);
} else {
graph_->SetNodePosition(node_, relevant_, old_pos_);
context_->SetNodePositionInContext(node_, old_pos_);
}
}
void NodeSetPositionAsChildCommand::redo()
{
if (!sub_command_) {
// Calculate position of node
NodeGraph *graph = parent_->parent();
QPointF pos = graph->GetNodePosition(parent_, relative_);
// This is a dependency, so we'll place it one X before
pos.setX(pos.x() - 1);
// The Y will be calculated using the index and child count
pos.setY(pos.y() - (double(child_count_)*0.5) + this_index_ + 0.5);
sub_command_ = new MultiUndoCommand();
if (shift_surroundings_) {
sub_command_->add_child(new NodeSetPositionAndShiftSurroundingsCommand(node_, relative_, pos, true));
} else {
sub_command_->add_child(new NodeSetPositionCommand(node_, relative_, pos, true));
}
}
sub_command_->redo_now();
}
void NodeSetPositionToOffsetOfAnotherNodeCommand::redo()
{
NodeGraph *graph = node_->parent();
old_pos_ = graph->GetNodePosition(node_, relative_);
graph->SetNodePosition(node_, relative_, graph->GetNodePosition(other_node_, relative_) + offset_);
}
void NodeSetPositionToOffsetOfAnotherNodeCommand::undo()
{
NodeGraph *graph = node_->parent();
graph->SetNodePosition(node_, relative_, old_pos_);
}
void NodeRemovePositionFromContextCommand::redo()
{
NodeGraph *graph = node_->parent();
contained_ = graph->ContextContainsNode(node_, context_);
contained_ = context_->ContextContainsNode(node_);
if (contained_) {
old_pos_ = graph->GetNodePosition(node_, context_);
graph->RemoveNodePosition(node_, context_);
old_pos_ = context_->GetNodePositionDataInContext(node_);
context_->RemoveNodeFromContext(node_);
}
}
void NodeRemovePositionFromContextCommand::undo()
{
if (contained_) {
NodeGraph *graph = node_->parent();
graph->SetNodePosition(node_, context_, old_pos_);
context_->SetNodePositionInContext(node_, old_pos_);
}
}
@@ -2438,28 +2412,21 @@ void NodeRemovePositionFromAllContextsCommand::redo()
{
NodeGraph *graph = node_->parent();
if (points_.empty()) {
// No points yet, let's see what points we should remove
auto map = graph->GetPositionMap();
for (auto it=map.cbegin(); it!=map.cend(); it++) {
if (it.value().contains(node_)) {
points_.insert({it.key(), it.value().value(node_)});
}
foreach (Node* context, graph->nodes()) {
if (context->ContextContainsNode(node_)) {
contexts_.insert({context, context->GetNodePositionInContext(node_)});
context->RemoveNodeFromContext(node_);
}
}
for (auto it=points_.cbegin(); it!=points_.cend(); it++) {
graph->RemoveNodePosition(node_, it->first);
}
}
void NodeRemovePositionFromAllContextsCommand::undo()
{
NodeGraph *graph = node_->parent();
for (auto it=points_.crbegin(); it!=points_.crend(); it++) {
graph->SetNodePosition(node_, it->first, it->second);
for (auto it = contexts_.crbegin(); it != contexts_.crend(); it++) {
it->first->SetNodePositionInContext(node_, it->second);
}
contexts_.clear();
}
void Node::ValueHint::Hash(QCryptographicHash &hash) const
@@ -2508,4 +2475,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);
}
}
}
}
+133 -170
View File
@@ -27,6 +27,7 @@
#include <QObject>
#include <QPainter>
#include <QPointF>
#include <QUuid>
#include <QXmlStreamWriter>
#include "codec/frame.h"
@@ -93,6 +94,11 @@ public:
kCategoryCount
};
enum Flag {
kNone = 0,
kDontShowInParamView = 0x1
};
Node();
virtual ~Node() override;
@@ -112,6 +118,14 @@ public:
Project* project() const;
const QUuid &GetUUID() const {return uuid_;}
void SetUUID(const QUuid &uuid) {uuid_ = uuid;}
const uint64_t &GetFlags() const
{
return flags_;
}
/**
* @brief Clear current node variables and replace them with
*/
@@ -208,6 +222,79 @@ public:
return HasInputWithID(id);
}
struct Position
{
Position(const QPointF &p = QPointF(0, 0), bool e = false)
{
position = p;
expanded = e;
}
QPointF position;
bool expanded;
inline Position &operator+=(const Position &p)
{
position += p.position;
return *this;
}
inline Position &operator-=(const Position &p)
{
position -= p.position;
return *this;
}
friend inline const Position operator+(Position a, const Position &b)
{
a += b;
return a;
}
friend inline const Position operator-(Position a, const Position &b)
{
a -= b;
return a;
}
};
using PositionMap = QHash<Node*, Position>;
const PositionMap &GetContextPositions() const
{
return context_positions_;
}
bool IsNodeExpandedInContext(Node *node) const
{
return context_positions_.value(node).expanded;
}
bool ContextContainsNode(Node *node) const
{
return context_positions_.contains(node);
}
Position GetNodePositionDataInContext(Node *node)
{
return context_positions_.value(node);
}
QPointF GetNodePositionInContext(Node *node)
{
return GetNodePositionDataInContext(node).position;
}
bool SetNodePositionInContext(Node *node, const QPointF &pos);
bool SetNodePositionInContext(Node *node, const Position &pos);
void SetNodeExpandedInContext(Node *node, bool e)
{
context_positions_[node].expanded = e;
}
bool RemoveNodeFromContext(Node *node);
/**
* @brief Retrieve the color of this node
*/
@@ -243,11 +330,12 @@ 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;
bool IsInputHidden(const QString& input) const;
bool IsInputConnectable(const QString& input) const;
bool IsInputKeyframable(const QString& input) const;
@@ -767,7 +855,7 @@ public:
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF& p);
virtual void GizmoMove(const QPointF& p, const rational &time, const Qt::KeyboardModifiers &modifiers);
virtual void GizmoRelease();
virtual void GizmoRelease(MultiUndoCommand *command);
const QString& GetLabel() const;
void SetLabel(const QString& s);
@@ -861,37 +949,9 @@ public:
};
InputFlags GetInputFlags(const QString& input) const;
protected:
enum InputFlag {
/// By default, inputs are keyframable, connectable, and NOT arrays
kInputFlagNormal = 0x0,
kInputFlagArray = 0x1,
kInputFlagNotKeyframable = 0x2,
kInputFlagNotConnectable = 0x4
};
class InputFlags {
public:
explicit InputFlags()
{
f_ = kInputFlagNormal;
}
explicit InputFlags(uint64_t flags)
{
f_ = flags;
}
bool operator&(const InputFlag& f) const
{
return f_ & f;
}
private:
uint64_t f_;
};
virtual void Hash(QCryptographicHash& hash, const NodeGlobals &globals, const VideoParams& video_params) const;
void HashAddNodeSignature(QCryptographicHash &hash) const;
@@ -986,12 +1046,12 @@ protected:
tooltip_ = s;
}
signals:
/**
* @brief Signal emitted whenever the position is set through SetPosition()
*/
void PositionChanged(const QPointF& pos);
void SetFlags(const uint64_t &f)
{
flags_ = f;
}
signals:
/**
* @brief Signal emitted when SetLabel() is called
*/
@@ -1021,7 +1081,11 @@ signals:
void KeyframeRemoved(NodeKeyframe* key);
void KeyframeTimeChanged();
void KeyframeTimeChanged(NodeKeyframe* key);
void KeyframeTypeChanged(NodeKeyframe* key);
void KeyframeValueChanged(NodeKeyframe* key);
void KeyframeEnableChanged(const NodeInput& input, bool enabled);
@@ -1037,6 +1101,12 @@ signals:
void RemovedFromGraph(NodeGraph* graph);
void NodeAddedToContext(Node *node);
void NodePositionInContextChanged(Node *node, const QPointF &pos);
void NodeRemovedFromContext(Node *node);
private:
class ArrayInsertCommand : public UndoCommand
{
@@ -1140,8 +1210,6 @@ private:
return input_ids_.indexOf(input);
}
InputFlags GetInputFlags(const QString& input) const;
Input* GetInternalInputData(const QString& input)
{
int i = GetInternalInputIndex(input);
@@ -1258,6 +1326,12 @@ private:
QMap<InputElementPair, ValueHint> value_hints_;
PositionMap context_positions_;
QUuid uuid_;
uint64_t flags_;
private slots:
/**
* @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time
@@ -1367,12 +1441,11 @@ using NodePtr = std::shared_ptr<Node>;
class NodeSetPositionCommand : public UndoCommand
{
public:
NodeSetPositionCommand(Node* node, Node* relevant, const QPointF& pos, bool move_dependencies_relatively)
NodeSetPositionCommand(Node* node, Node* context, const Node::Position& pos)
{
node_ = node;
relevant_ = relevant;
context_ = context;
pos_ = pos;
move_deps_ = move_dependencies_relatively;
}
virtual Project* GetRelevantProject() const override
@@ -1387,153 +1460,43 @@ protected:
private:
Node* node_;
Node* relevant_;
QPointF pos_;
QPointF old_pos_;
Node* context_;
Node::Position pos_;
Node::Position old_pos_;
bool added_;
bool move_deps_;
NodeGraph *graph_;
};
class NodeSetPositionAndShiftSurroundingsCommand : public UndoCommand
{
class NodeSetPositionAndDependenciesRecursivelyCommand : public UndoCommand{
public:
NodeSetPositionAndShiftSurroundingsCommand(Node* node, Node *relative, const QPointF& pos, bool move_dependencies_relatively) :
NodeSetPositionAndDependenciesRecursivelyCommand(Node* node, Node* context, const Node::Position& pos) :
node_(node),
relative_(relative),
position_(pos),
move_dependencies_(move_dependencies_relatively)
context_(context),
pos_(pos)
{}
virtual ~NodeSetPositionAndShiftSurroundingsCommand() override
{
qDeleteAll(commands_);
}
virtual Project * GetRelevantProject() const override
virtual Project* GetRelevantProject() const override
{
return node_->project();
}
protected:
virtual void prepare() override;
virtual void redo() override;
virtual void undo() override
{
for (int i=commands_.size()-1; i>=0; i--) {
commands_.at(i)->undo_now();
}
}
virtual void undo() override;
private:
void move_recursively(Node *node, const QPointF &diff);
Node* node_;
Node *relative_;
QPointF position_;
bool move_dependencies_;
Node* context_;
Node::Position pos_;
QVector<UndoCommand*> commands_;
};
class NodeSetPositionAsChildCommand : public UndoCommand
{
public:
NodeSetPositionAsChildCommand(Node* node, Node* parent, Node *relative, double this_index, int child_count, bool shift_surroundings) :
node_(node),
parent_(parent),
relative_(relative),
this_index_(this_index),
child_count_(child_count),
shift_surroundings_(shift_surroundings),
sub_command_(nullptr)
{
}
virtual ~NodeSetPositionAsChildCommand() override
{
delete sub_command_;
}
virtual Project * GetRelevantProject() const override
{
return node_->project();
}
protected:
virtual void redo() override;
virtual void undo() override
{
sub_command_->undo_now();
}
private:
Node* node_;
Node* parent_;
Node *relative_;
double this_index_;
int child_count_;
bool shift_surroundings_;
MultiUndoCommand* sub_command_;
};
class NodePositionCloseChildGapCommand : public UndoCommand
{
public:
NodePositionCloseChildGapCommand(Node *parent, void *relative, int remove_index, int child_count, bool shift_surroundings);
virtual Project * GetRelevantProject() const override
{
return parent_->project();
}
protected:
virtual void redo() override;
virtual void undo() override;
private:
Node *parent_;
};
class NodeSetPositionToOffsetOfAnotherNodeCommand : public UndoCommand
{
public:
NodeSetPositionToOffsetOfAnotherNodeCommand(Node* node, Node* other_node, Node *relative, const QPointF& offset) :
node_(node),
other_node_(other_node),
relative_(relative),
offset_(offset)
{}
virtual Project * GetRelevantProject() const override
{
return node_->project();
}
protected:
virtual void redo() override;
virtual void undo() override;
private:
Node* node_;
Node* other_node_;
Node *relative_;
QPointF offset_;
QPointF old_pos_;
};
class NodeRemovePositionFromContextCommand : public UndoCommand
{
public:
@@ -1558,7 +1521,7 @@ private:
Node *context_;
QPointF old_pos_;
Node::Position old_pos_;
bool contained_;
@@ -1585,7 +1548,7 @@ protected:
private:
Node *node_;
std::map<Node *, QPointF> points_;
std::map<Node *, QPointF> contexts_;
};
+32 -18
View File
@@ -29,7 +29,7 @@
namespace olive {
void NodeCopyPasteService::CopyNodesToClipboard(const QVector<Node *> &nodes, void *userdata)
void NodeCopyPasteService::CopyNodesToClipboard(QVector<Node *> nodes, void *userdata)
{
QString copy_str;
@@ -42,22 +42,33 @@ void NodeCopyPasteService::CopyNodesToClipboard(const QVector<Node *> &nodes, vo
writer.writeTextElement(QStringLiteral("version"), QString::number(Core::kProjectVersion));
writer.writeStartElement(QStringLiteral("nodes"));
foreach (Node* n, nodes) {
for (int i=0; i<nodes.size(); i++) {
Node *n = nodes.at(i);
writer.writeStartElement(QStringLiteral("node"));
writer.writeAttribute(QStringLiteral("id"), n->id());
n->Save(&writer);
writer.writeEndElement(); // node
// If this is a group, add the child nodes too
if (NodeGroup *g = dynamic_cast<NodeGroup*>(n)) {
for (auto it=g->GetContextPositions().cbegin(); it!=g->GetContextPositions().cend(); it++) {
if (!nodes.contains(it.key())) {
nodes.append(it.key());
}
}
}
}
writer.writeEndElement(); // nodes
writer.writeStartElement(QStringLiteral("contexts"));
foreach (Node* n, nodes) {
// Determine if this node is a context
if (n->parent()->GetPositionMap().contains(n)) {
if (!n->GetContextPositions().isEmpty()) {
writer.writeStartElement(QStringLiteral("context"));
writer.writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(n)));
const NodeGraph::PositionMap &map = n->parent()->GetNodesForContext(n);
const Node::PositionMap &map = n->GetContextPositions();
for (auto it=map.cbegin(); it!=map.cend(); it++) {
writer.writeStartElement(QStringLiteral("node"));
Project::SavePosition(&writer, it.key(), it.value());
@@ -92,7 +103,7 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
QVector<Node*> pasted_nodes;
XMLNodeData xml_node_data;
QMap<quintptr, QMap<quintptr, QPointF> > pasted_contexts;
QMap<quintptr, QMap<quintptr, Node::Position> > pasted_contexts;
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("olive")) {
@@ -131,7 +142,7 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("context")) {
// Get context ptr
QMap<quintptr, QPointF> map;
QMap<quintptr, Node::Position> map;
quintptr context_ptr = 0;
XMLAttributeLoop((&reader), attr) {
if (attr.name() == QStringLiteral("ptr")) {
@@ -144,7 +155,7 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("node")) {
quintptr node_ptr;
QPointF node_pos;
Node::Position node_pos;
if (Project::LoadPosition(&reader, &node_ptr, &node_pos)) {
map.insert(node_ptr, node_pos);
@@ -201,31 +212,34 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
// Add all nodes to graph
foreach (Node* n, pasted_nodes) {
command->add_child(new NodeAddCommand(graph, n));
if (command) {
command->add_child(new NodeAddCommand(graph, n));
} else {
n->setParent(graph);
}
}
// Make connections
if (!xml_node_data.desired_connections.isEmpty()) {
XMLConnectNodes(xml_node_data, data_version, command);
}
// Link blocks
XMLLinkBlocks(xml_node_data);
// Process contexts
for (auto it=pasted_contexts.cbegin(); it!=pasted_contexts.cend(); it++) {
Node *context = xml_node_data.node_ptrs.value(it.key());
if (context) {
const QMap<quintptr, QPointF> &map = it.value();
auto map = it.value();
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
Node *subnode = xml_node_data.node_ptrs.value(jt.key());
if (subnode) {
command->add_child(new NodeSetPositionCommand(subnode, context, jt.value(), false));
if (command) {
command->add_child(new NodeSetPositionCommand(subnode, context, jt.value()));
} else {
context->SetNodePositionInContext(subnode, jt.value());
}
}
}
}
}
// Make connections
xml_node_data.PostConnect(data_version, command);
return pasted_nodes;
}
+1 -1
View File
@@ -35,7 +35,7 @@ public:
NodeCopyPasteService() = default;
protected:
void CopyNodesToClipboard(const QVector<Node *> &nodes, void* userdata = nullptr);
void CopyNodesToClipboard(QVector<Node *> nodes, void* userdata = nullptr);
QVector<Node*> PasteNodesFromClipboard(NodeGraph *graph, MultiUndoCommand *command, void* userdata = nullptr);
+3 -2
View File
@@ -41,9 +41,10 @@ const QString Track::kMutedInput = QStringLiteral("muted_in");
Track::Track() :
track_type_(Track::kNone),
index_(-1),
locked_(false)
locked_(false),
sequence_(nullptr)
{
AddInput(kBlockInput, NodeValue::kNone, InputFlags(kInputFlagArray | kInputFlagNotKeyframable));
AddInput(kBlockInput, NodeValue::kNone, InputFlags(kInputFlagArray | kInputFlagNotKeyframable | kInputFlagHidden));
// Since blocks are time based, we can handle the invalidate timing a little more intelligently
// on our end
+53 -9
View File
@@ -26,6 +26,8 @@
namespace olive {
class Sequence;
/**
* @brief A time traversal Node for sorting through one channel/track of Blocks
*/
@@ -168,17 +170,48 @@ public:
QString ToString() const
{
QString type_string;
if (type_ == Track::kVideo) {
type_string = QStringLiteral("v");
} else if (type_ == Track::kAudio) {
type_string = QStringLiteral("a");
} else {
QString type_string = TypeToString(type_);
if (type_string.isEmpty()) {
return QString();
} else {
return QStringLiteral("%1:%2").arg(type_string, QString::number(index_));
}
}
/// For IDs that shouldn't change between localizations
static QString TypeToString(Type type)
{
switch (type) {
case kVideo:
return QStringLiteral("v");
case kAudio:
return QStringLiteral("a");
case kSubtitle:
return QStringLiteral("s");
case kCount:
case kNone:
break;
}
return QStringLiteral("%1:%2").arg(type_string, QString::number(index_));
return QString();
}
/// For human-facing strings
static QString TypeToTranslatedString(Type type)
{
switch (type) {
case kVideo:
return tr("V");
case kAudio:
return tr("A");
case kSubtitle:
return tr("S");
case kCount:
case kNone:
break;
}
return QString();
}
static Type TypeFromString(const QString& s)
@@ -359,9 +392,18 @@ public:
bool IsLocked() const;
int GetArrayIndexFromBlock(Block* block) const;
Sequence *sequence() const
{
return sequence_;
}
void set_sequence(Sequence *sequence)
{
sequence_ = sequence;
}
static const double kTrackHeightDefault;
static const double kTrackHeightMinimum;
static const double kTrackHeightInterval;
@@ -443,6 +485,8 @@ private:
bool locked_;
Sequence *sequence_;
private slots:
void BlockLengthChanged();
+2
View File
@@ -83,6 +83,7 @@ void TrackList::TrackConnected(Node *node, int element)
connect(track, &Track::TrackLengthChanged, this, &TrackList::UpdateTotalLength);
track->set_type(type_);
track->set_sequence(parent());
emit TrackListChanged();
@@ -121,6 +122,7 @@ void TrackList::TrackDisconnected(Node *node, int element)
track->SetIndex(-1);
track->set_type(Track::kNone);
track->set_sequence(nullptr);
disconnect(track, &Track::TrackLengthChanged, this, &TrackList::UpdateTotalLength);
+4 -6
View File
@@ -23,7 +23,6 @@
#include "config/config.h"
#include "core.h"
#include "node/traverser.h"
#include "widget/videoparamedit/videoparamedit.h"
namespace olive {
@@ -34,8 +33,6 @@ const QString ViewerOutput::kSamplesInput = QStringLiteral("samples_in");
const QString ViewerOutput::kVideoAutoCacheInput = QStringLiteral("video_autocache_in");
const QString ViewerOutput::kAudioAutoCacheInput = QStringLiteral("audio_autocache_in");
const uint64_t ViewerOutput::kVideoParamEditMask = VideoParamEdit::kWidthHeight | VideoParamEdit::kInterlacing | VideoParamEdit::kFrameRate | VideoParamEdit::kPixelAspect;
#define super Node
ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_streams) :
@@ -47,10 +44,9 @@ ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_stream
video_cache_enabled_(true),
audio_cache_enabled_(true)
{
AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray));
SetInputProperty(kVideoParamsInput, QStringLiteral("mask"), QVariant::fromValue(kVideoParamEditMask));
AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden));
AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray));
AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden));
if (create_buffer_inputs) {
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
@@ -70,6 +66,8 @@ ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_stream
AddStream(Track::kAudio, QVariant());
set_default_parameters();
}
SetFlags(kDontShowInParamView);
}
Node *ViewerOutput::copy() const
-4
View File
@@ -200,8 +200,6 @@ public:
static const QString kVideoAutoCacheInput;
static const QString kAudioAutoCacheInput;
static const uint64_t kVideoParamEditMask;
signals:
void FrameRateChanged(const rational&);
@@ -254,8 +252,6 @@ private:
AudioPlaybackCache audio_playback_cache_;
int operation_stack_;
VideoParams cached_video_params_;
AudioParams cached_audio_params_;
+36
View File
@@ -33,6 +33,15 @@ QString NodeInput::name() const
}
}
bool NodeInput::IsHidden() const
{
if (IsValid()) {
return node_->IsInputHidden(input_);
} else {
return false;
}
}
bool NodeInput::IsConnected() const
{
if (IsValid()) {
@@ -60,6 +69,15 @@ bool NodeInput::IsArray() const
}
}
InputFlags NodeInput::GetFlags() const
{
if (IsValid()) {
return node_->GetInputFlags(input_);
} else {
return InputFlags(kInputFlagNormal);
}
}
Node *NodeInput::GetConnectedOutput() const
{
if (IsValid()) {
@@ -78,6 +96,15 @@ NodeValue::Type NodeInput::GetDataType() const
}
}
QVariant NodeInput::GetDefaultValue() const
{
if (IsValid()) {
return node_->GetDefaultValue(input_);
} else {
return QVariant();
}
}
QStringList NodeInput::GetComboBoxStrings() const
{
if (IsValid()) {
@@ -123,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());
+50 -1
View File
@@ -31,6 +31,37 @@ namespace olive {
class Node;
class NodeKeyframe;
enum InputFlag {
/// By default, inputs are keyframable, connectable, and NOT arrays
kInputFlagNormal = 0x0,
kInputFlagArray = 0x1,
kInputFlagNotKeyframable = 0x2,
kInputFlagNotConnectable = 0x4,
kInputFlagHidden = 0x8
};
class InputFlags {
public:
explicit InputFlags()
{
f_ = kInputFlagNormal;
}
explicit InputFlags(uint64_t flags)
{
f_ = flags;
}
bool operator&(const InputFlag& f) const
{
return f_ & f;
}
private:
uint64_t f_;
};
struct NodeInputPair {
bool operator==(const NodeInputPair& rhs) const
{
@@ -98,11 +129,21 @@ public:
return input_;
}
int element() const
const int &element() const
{
return element_;
}
void set_node(Node *node)
{
node_ = node;
}
void set_input(const QString &input)
{
input_ = input;
}
void set_element(int e)
{
element_ = e;
@@ -115,16 +156,22 @@ public:
return node_ && !input_.isEmpty() && element_ >= -1;
}
bool IsHidden() const;
bool IsConnected() const;
bool IsKeyframing() const;
bool IsArray() const;
InputFlags GetFlags() const;
Node *GetConnectedOutput() const;
NodeValue::Type GetDataType() const;
QVariant GetDefaultValue() const;
QStringList GetComboBoxStrings() const;
QVariant GetProperty(const QString& key) const;
@@ -135,6 +182,8 @@ public:
QVariant GetSplitDefaultValueForTrack(int track) const;
int GetArraySize() const;
void Reset()
{
*this = NodeInput();
+2 -20
View File
@@ -113,19 +113,12 @@ void Folder::InputDisconnectedEvent(const QString &input, int element, Node *out
}
}
FolderAddChild::FolderAddChild(Folder *folder, Node *child, bool autoposition) :
FolderAddChild::FolderAddChild(Folder *folder, Node *child) :
folder_(folder),
child_(child),
autoposition_(autoposition),
position_command_(nullptr)
child_(child)
{
}
FolderAddChild::~FolderAddChild()
{
delete position_command_;
}
Project *FolderAddChild::GetRelevantProject() const
{
return folder_->project();
@@ -136,21 +129,10 @@ void FolderAddChild::redo()
int array_index = folder_->InputArraySize(Folder::kChildInput);
folder_->InputArrayAppend(Folder::kChildInput, false);
Node::ConnectEdge(child_, NodeInput(folder_, Folder::kChildInput, array_index));
if (autoposition_) {
if (!position_command_) {
position_command_ = new NodeSetPositionAsChildCommand(child_, folder_, folder_->project()->root(), array_index, array_index+1, true);
}
position_command_->redo_now();
}
}
void FolderAddChild::undo()
{
if (position_command_) {
position_command_->undo_now();
}
Node::DisconnectEdge(child_, NodeInput(folder_, Folder::kChildInput, folder_->InputArraySize(Folder::kChildInput)-1));
folder_->InputArrayRemoveLast(Folder::kChildInput);
}
+1 -7
View File
@@ -208,9 +208,7 @@ private:
class FolderAddChild : public UndoCommand
{
public:
FolderAddChild(Folder* folder, Node* child, bool autoposition = true);
virtual ~FolderAddChild() override;
FolderAddChild(Folder* folder, Node* child);
virtual Project * GetRelevantProject() const override;
@@ -224,10 +222,6 @@ private:
Node* child_;
bool autoposition_;
NodeSetPositionAsChildCommand* position_command_;
};
}
-34
View File
@@ -33,7 +33,6 @@
#include "core.h"
#include "render/job/footagejob.h"
#include "ui/icons/icons.h"
#include "widget/videoparamedit/videoparamedit.h"
namespace olive {
@@ -144,39 +143,6 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
AddStream(Track::kVideo, QVariant::fromValue(vp));
}
if (!footage_info.GetVideoStreams().isEmpty()) {
// FIXME: This will break on multiple video streams. Currently we don't have
// infrastructure for different properties per element. We'll see if this becomes
// a problem.
VideoParams vp = footage_info.GetVideoStreams().first();
uint64_t video_param_mask = 0;
video_param_mask |= VideoParamEdit::kEnabled;
video_param_mask |= VideoParamEdit::kColorspace;
video_param_mask |= VideoParamEdit::kPixelAspect;
video_param_mask |= VideoParamEdit::kInterlacing;
video_param_mask |= VideoParamEdit::kFrameRateIsArbitrary;
if (vp.channel_count() == VideoParams::kRGBAChannelCount) {
// Add premultiplied setting if this footage has an alpha channel
video_param_mask |= VideoParamEdit::kPremultipliedAlpha;
}
if (vp.video_type() == VideoParams::kVideoTypeVideo) {
// This is video, ensure that the frame rate does not overwrite the timebase
video_param_mask |= VideoParamEdit::kFrameRateIsNotTimebase;
} else {
// This is not a video, so it's either a still image or an image sequence
video_param_mask |= VideoParamEdit::kIsImageSequence;
video_param_mask |= VideoParamEdit::kStartTime;
video_param_mask |= VideoParamEdit::kEndTime;
video_param_mask |= VideoParamEdit::kFrameRate;
}
SetInputProperty(kVideoParamsInput, QStringLiteral("mask"), QVariant::fromValue(video_param_mask));
}
for (int i=0; i<footage_info.GetAudioStreams().size(); i++) {
AddStream(Track::kAudio, QVariant::fromValue(footage_info.GetAudioStreams().at(i)));
}
+24 -25
View File
@@ -44,19 +44,16 @@ Project::Project() :
root_->setParent(this);
root_->SetLabel(tr("Root"));
root_->SetCanBeDeleted(false);
SetNodePosition(root_, root_, QPointF(0, 0));
// Adds a color manager "node" to this project so that it synchronizes
color_manager_ = new ColorManager();
color_manager_->setParent(this);
SetNodePosition(color_manager_, root_, QPointF(1, 0));
color_manager_->SetCanBeDeleted(false);
AddDefaultNode(color_manager_);
// Same with project settings
settings_ = new ProjectSettingsNode();
settings_->setParent(this);
SetNodePosition(settings_, root_, QPointF(2, 0));
settings_->SetCanBeDeleted(false);
AddDefaultNode(settings_);
@@ -161,13 +158,13 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
quintptr node_ptr;
QPointF node_pos;
Node::Position node_pos;
if (LoadPosition(reader, &node_ptr, &node_pos)) {
Node *node = xml_node_data.node_ptrs.value(node_ptr);
if (node) {
SetNodePosition(node, context, node_pos);
context->SetNodePositionInContext(node, node_pos);
} else {
qWarning() << "Failed to find pointer for node position";
reader->skipCurrentElement();
@@ -196,10 +193,7 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
}
// Make connections
XMLConnectNodes(xml_node_data, version);
// Link blocks
XMLLinkBlocks(xml_node_data);
xml_node_data.PostConnect(version);
}
void Project::Save(QXmlStreamWriter *writer) const
@@ -230,20 +224,22 @@ void Project::Save(QXmlStreamWriter *writer) const
writer->writeStartElement(QStringLiteral("positions"));
for (auto it=GetPositionMap().cbegin(); it!=GetPositionMap().cend(); it++) {
writer->writeStartElement(QStringLiteral("context"));
foreach (Node* context, nodes()) {
const Node::PositionMap &map = context->GetContextPositions();
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(it.key())));
if (!map.isEmpty()) {
writer->writeStartElement(QStringLiteral("context"));
const PositionMap &map = it.value();
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(context)));
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
writer->writeStartElement(QStringLiteral("node"));
SavePosition(writer, jt.key(), jt.value());
writer->writeEndElement(); // node
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
writer->writeStartElement(QStringLiteral("node"));
SavePosition(writer, jt.key(), jt.value());
writer->writeEndElement(); // node
}
writer->writeEndElement(); // context
}
writer->writeEndElement(); // context
}
writer->writeEndElement(); // positions
@@ -351,7 +347,7 @@ void Project::RegenerateUuid()
uuid_ = QUuid::createUuid();
}
bool Project::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, QPointF *pos)
bool Project::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos)
{
bool got_node_ptr = false;
bool got_pos_x = false;
@@ -367,11 +363,13 @@ bool Project::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, QPointF
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("x")) {
pos->setX(reader->readElementText().toDouble());
pos->position.setX(reader->readElementText().toDouble());
got_pos_x = true;
} else if (reader->name() == QStringLiteral("y")) {
pos->setY(reader->readElementText().toDouble());
pos->position.setY(reader->readElementText().toDouble());
got_pos_y = true;
} else if (reader->name() == QStringLiteral("expanded")) {
pos->expanded = reader->readElementText().toInt();
} else {
reader->skipCurrentElement();
}
@@ -380,12 +378,13 @@ bool Project::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, QPointF
return got_node_ptr && got_pos_x && got_pos_y;
}
void Project::SavePosition(QXmlStreamWriter *writer, Node *node, const QPointF &pos)
void Project::SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos)
{
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(node)));
writer->writeTextElement(QStringLiteral("x"), QString::number(pos.x()));
writer->writeTextElement(QStringLiteral("y"), QString::number(pos.y()));
writer->writeTextElement(QStringLiteral("x"), QString::number(pos.position.x()));
writer->writeTextElement(QStringLiteral("y"), QString::number(pos.position.y()));
writer->writeTextElement(QStringLiteral("expanded"), QString::number(pos.expanded));
}
void Project::ColorManagerValueChanged(const NodeInput &input, const TimeRange &range)
+2 -2
View File
@@ -82,8 +82,8 @@ public:
void RegenerateUuid();
static bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, QPointF *pos);
static void SavePosition(QXmlStreamWriter *writer, Node *node, const QPointF &pos);
static bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos);
static void SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos);
signals:
void NameChanged();
+1 -1
View File
@@ -41,7 +41,7 @@ Sequence::Sequence()
// Create track input
QString track_input_id = kTrackInputFormat.arg(i);
AddInput(track_input_id, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray));
AddInput(track_input_id, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden));
IgnoreInvalidationsFrom(track_input_id);