Merge branch 'nodeview-redux'

This commit is contained in:
itsmattkc
2021-07-19 18:41:50 -07:00
126 changed files with 6691 additions and 4118 deletions
+24 -2
View File
@@ -22,6 +22,7 @@
#include <QDebug>
#include "core.h"
#include "node/output/track/track.h"
#include "transition/transition.h"
#include "widget/slider/floatslider.h"
@@ -29,6 +30,8 @@
namespace olive {
#define super Node
const QString Block::kLengthInput = QStringLiteral("length_in");
const QString Block::kMediaInInput = QStringLiteral("media_in_in");
const QString Block::kEnabledInput = QStringLiteral("enabled_in");
@@ -47,7 +50,6 @@ Block::Block() :
SetInputProperty(kLengthInput, QStringLiteral("min"), QVariant::fromValue(rational(0, 1)));
SetInputProperty(kLengthInput, QStringLiteral("view"), RationalSlider::kTime);
SetInputProperty(kLengthInput, QStringLiteral("viewlock"), true);
IgnoreInvalidationsFrom(kLengthInput);
IgnoreHashingFrom(kLengthInput);
AddInput(kMediaInInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
@@ -221,7 +223,7 @@ void Block::set_length_internal(const rational &length)
void Block::Retranslate()
{
Node::Retranslate();
super::Retranslate();
SetInputName(kLengthInput, tr("Length"));
SetInputName(kMediaInInput, tr("Media In"));
@@ -235,4 +237,24 @@ void Block::Hash(const QString &, QCryptographicHash &, const rational &, const
// A block does nothing by default, so we hash nothing
}
void Block::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options)
{
TimeRange r;
if (from == kLengthInput) {
// We must intercept the signal here
r = TimeRange(qMin(length(), last_length_), RATIONAL_MAX);
if (!Core::instance()->EffectsSliderIsBeingDragged()) {
last_length_ = length();
}
options.insert(QStringLiteral("lengthevent"), true);
} else {
r = range;
}
super::InvalidateCache(r, from, element, options);
}
}
+4
View File
@@ -155,6 +155,8 @@ public:
virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time, const VideoParams& video_params) const override;
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1, InvalidateCacheOptions options = InvalidateCacheOptions()) override;
static const QString kLengthInput;
static const QString kMediaInInput;
static const QString kEnabledInput;
@@ -193,6 +195,8 @@ private:
QVector<Block*> block_links_;
rational last_length_;
};
}
+3 -3
View File
@@ -53,7 +53,7 @@ QString ClipBlock::Description() const
return tr("A time-based node that represents a media source.");
}
void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time)
void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options)
{
Q_UNUSED(element)
@@ -63,10 +63,10 @@ void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int
rational start = MediaToSequenceTime(range.in());
rational end = MediaToSequenceTime(range.out());
super::InvalidateCache(TimeRange(start, end), from, element, job_time);
super::InvalidateCache(TimeRange(start, end), from, element, options);
} else {
// Otherwise, pass signal along normally
super::InvalidateCache(range, from, element, job_time);
super::InvalidateCache(range, from, element, options);
}
}
+1 -1
View File
@@ -42,7 +42,7 @@ public:
virtual QString id() const override;
virtual QString Description() const override;
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time) override;
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) override;
virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override;
+51
View File
@@ -44,6 +44,45 @@ 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 count = 0;
for (auto it=position_map_.cbegin(); it!=position_map_.cend(); it++) {
if (it.value().contains(node)) {
count++;
}
}
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);
@@ -75,6 +114,18 @@ void NodeGraph::childEvent(QChildEvent *event)
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++;
}
}
}
}
}
}
+57
View File
@@ -63,6 +63,55 @@ 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 = QMap<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;
signals:
/**
* @brief Signal emitted when a Node is added to the graph
@@ -80,6 +129,10 @@ signals:
void ValueChanged(const NodeInput& input);
void NodePositionAdded(Node *node, Node *relative, const QPointF &position);
void NodePositionRemoved(Node *node, Node *relative);
protected:
void AddDefaultNode(Node* n)
{
@@ -93,6 +146,10 @@ private:
QVector<Node*> default_nodes_;
QMap<Node *, PositionMap> position_map_;
PositionMap root_position_map_;
};
}
+167 -85
View File
@@ -46,7 +46,6 @@ const QString Node::kDefaultOutput = QStringLiteral("output");
Node::Node(bool create_default_output) :
can_be_deleted_(true),
override_color_(-1),
last_change_time_(0),
folder_(nullptr),
operation_stack_(0),
cache_result_(false)
@@ -91,20 +90,6 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint versi
LoadInput(reader, xml_node_data, cancelled);
} else if (reader->name() == QStringLiteral("ptr")) {
xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), this);
} else if (reader->name() == QStringLiteral("pos")) {
QPointF p;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("x")) {
p.setX(reader->readElementText().toDouble());
} else if (reader->name() == QStringLiteral("y")) {
p.setY(reader->readElementText().toDouble());
} else {
reader->skipCurrentElement();
}
}
SetPosition(p);
} else if (reader->name() == QStringLiteral("label")) {
SetLabel(reader->readElementText());
} else if (reader->name() == QStringLiteral("color")) {
@@ -166,11 +151,6 @@ void Node::Save(QXmlStreamWriter *writer) const
{
writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
writer->writeStartElement(QStringLiteral("pos"));
writer->writeTextElement(QStringLiteral("x"), QString::number(GetPosition().x()));
writer->writeTextElement(QStringLiteral("y"), QString::number(GetPosition().y()));
writer->writeEndElement(); // pos
writer->writeTextElement(QStringLiteral("label"), GetLabel());
writer->writeTextElement(QStringLiteral("color"), QString::number(override_color_));
@@ -282,9 +262,6 @@ void Node::ConnectEdge(const NodeOutput &output, const NodeInput &input)
input.node()->input_connections_[input] = output;
output.node()->output_connections_.push_back(std::pair<NodeOutput, NodeInput>({output, input}));
// Update change times
input.node()->UpdateLastChangedTime();
// Call internal events
input.node()->InputConnectedEvent(input.input(), input.element(), output);
output.node()->OutputConnectedEvent(output.output(), input);
@@ -314,9 +291,6 @@ void Node::DisconnectEdge(const NodeOutput &output, const NodeInput &input)
OutputConnections& outputs = output.node()->output_connections_;
outputs.erase(std::find(outputs.begin(), outputs.end(), std::pair<NodeOutput, NodeInput>({output, input})));
// Update change times
input.node()->UpdateLastChangedTime();
// Call internal events
input.node()->InputDisconnectedEvent(input.input(), input.element(), output);
output.node()->OutputDisconnectedEvent(output.output(), input);
@@ -953,7 +927,7 @@ void Node::InputArrayResize(const QString &id, int size, bool undoable)
if (undoable) {
Core::instance()->undo_stack()->push(c);
} else {
c->redo();
c->redo_now();
delete c;
}
}
@@ -1045,12 +1019,12 @@ NodeValueTable Node::Value(const QString& output, NodeValueDatabase &value) cons
return value.Merge();
}
void Node::InvalidateCache(const TimeRange &range, const QString &from, int element, qint64 job_time)
void Node::InvalidateCache(const TimeRange &range, const QString &from, int element, InvalidateCacheOptions options)
{
Q_UNUSED(from)
Q_UNUSED(element)
SendInvalidateCache(range, job_time);
SendInvalidateCache(range, options);
}
void Node::BeginOperation()
@@ -1134,7 +1108,7 @@ void Node::CopyDependencyGraph(const QVector<Node *> &src, const QVector<Node *>
}
}
Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap<const Node*, Node*>& created, const Node *node, MultiUndoCommand *command)
Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap<Node*, Node*>& created, Node *node, MultiUndoCommand *command)
{
// Make a new node of the same type
Node* copy = node->copy();
@@ -1171,17 +1145,26 @@ Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap<const Node*, Node*
NodeInput(copy, 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));
}
}
return copy;
}
Node *Node::CopyNodeAndDependencyGraphMinusItems(const Node *node, MultiUndoCommand *command)
Node *Node::CopyNodeAndDependencyGraphMinusItems(Node *node, MultiUndoCommand *command)
{
QMap<const Node*, Node*> created;
QMap<Node*, Node*> created;
return CopyNodeAndDependencyGraphMinusItemsInternal(created, node, command);
}
Node *Node::CopyNodeInGraph(const Node *node, MultiUndoCommand *command)
Node *Node::CopyNodeInGraph(Node *node, MultiUndoCommand *command)
{
Node* copy;
@@ -1194,19 +1177,28 @@ Node *Node::CopyNodeInGraph(const Node *node, MultiUndoCommand *command)
copy));
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));
}
}
}
return copy;
}
void Node::SendInvalidateCache(const TimeRange &range, qint64 job_time)
void Node::SendInvalidateCache(const TimeRange &range, const InvalidateCacheOptions &options)
{
if (GetOperationStack() == 0) {
for (const OutputConnection& conn : output_connections_) {
// Send clear cache signal to the Node
const NodeInput& in = conn.second;
in.node()->InvalidateCache(range, in.input(), in.element(), job_time);
in.node()->InvalidateCache(range, in.input(), in.element(), options);
}
}
}
@@ -1482,7 +1474,6 @@ void Node::CopyInputs(const Node *source, Node *destination, bool include_connec
CopyInput(source, destination, input, include_connections, true);
}
destination->SetPosition(source->GetPosition());
destination->SetLabel(source->GetLabel());
destination->SetOverrideColor(source->GetOverrideColor());
}
@@ -1642,15 +1633,28 @@ void Node::GenerateFrame(FramePtr frame, const GenerateJob &job) const
Q_UNUSED(job)
}
bool Node::OutputsTo(Node *n, bool recursively) const
bool Node::OutputsTo(Node *n, bool recursively, const OutputConnections &ignore_edges, const OutputConnection &added_edge) const
{
for (const OutputConnection& conn : output_connections_) {
if (std::find(ignore_edges.cbegin(), ignore_edges.cend(), conn) != ignore_edges.cend()) {
// If this edge is in the "ignore edges" list, skip it
continue;
}
Node* connected = conn.second.node();
if (connected == n) {
return true;
} else if (recursively && connected->OutputsTo(n, recursively)) {
} else if (recursively && connected->OutputsTo(n, recursively, ignore_edges, added_edge)) {
return true;
} else if (added_edge.first.node() == this) {
Node *proposed_connected = added_edge.second.node();
if (proposed_connected == n) {
return true;
} else if (recursively && proposed_connected->OutputsTo(n, recursively, ignore_edges, added_edge)) {
return true;
}
}
}
@@ -1717,7 +1721,7 @@ bool Node::InputsFrom(const QString &id, bool recursively) const
return false;
}
int Node::GetRoutesTo(Node *n) const
int Node::GetNumberOfRoutesTo(Node *n) const
{
bool outputs_directly = false;
int routes = 0;
@@ -1728,7 +1732,7 @@ int Node::GetRoutesTo(Node *n) const
if (connected_node == n) {
outputs_directly = true;
} else {
routes += connected_node->GetRoutesTo(n);
routes += connected_node->GetNumberOfRoutesTo(n);
}
}
@@ -1831,33 +1835,8 @@ QVariant Node::PtrToValue(void *ptr)
return reinterpret_cast<quintptr>(ptr);
}
const QPointF &Node::GetPosition() const
{
return position_;
}
void Node::SetPosition(const QPointF &pos, bool move_dependencies_relatively_too)
{
QPointF old_pos = position_;
position_ = pos;
emit PositionChanged(position_);
if (move_dependencies_relatively_too) {
QPointF difference = pos - old_pos;
for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) {
Node* c = it->second.node();
c->SetPosition(c->GetPosition() + difference, true);
}
}
}
void Node::ParameterValueChanged(const QString& input, int element, const TimeRange& range)
{
UpdateLastChangedTime();
InputValueChangedEvent(input, element);
emit ValueChanged(NodeInput(this, input, element), range);
@@ -2049,11 +2028,6 @@ void Node::SaveImmediate(QXmlStreamWriter *writer, const QString& input, int ele
}
}
void Node::UpdateLastChangedTime()
{
last_change_time_ = QDateTime::currentMSecsSinceEpoch();
}
TimeRange Node::GetRangeAffectedByKeyframe(NodeKeyframe *key) const
{
const NodeKeyframeTrack& key_track = GetTrackFromKeyframe(key);
@@ -2302,36 +2276,144 @@ void NodeSetPositionAndShiftSurroundingsCommand::redo()
{
if (commands_.isEmpty()) {
// Move first node
NodeSetPositionCommand* set_pos_command = new NodeSetPositionCommand(node_, position_, move_dependencies_);
set_pos_command->redo();
NodeSetPositionCommand* set_pos_command = new NodeSetPositionCommand(node_, relative_, position_, move_dependencies_);
set_pos_command->redo_now();
commands_.append(set_pos_command);
// Get bounding rect
QRectF bounding_rect(position_.x() - 0.5, position_.y() - 0.5, 1, 1);
qreal bounding_rect_sz = 1.0;
qreal bounding_rect_half_sz = bounding_rect_sz * 0.5;
QRectF bounding_rect(position_.x() - bounding_rect_half_sz, position_.y() - bounding_rect_half_sz, bounding_rect_sz, bounding_rect_sz);
// Start moving other nodes
foreach (Node* surrounding, node_->parent()->nodes()) {
if (bounding_rect.contains(surrounding->GetPosition()) && surrounding != node_) {
QPointF new_pos = surrounding->GetPosition();
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;
qreal move_rate = 0.50;
if (surrounding->GetPosition().y() < position_.y()) {
move_rate = -move_rate;
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);
}
new_pos.setY(new_pos.y() + move_rate);
auto sur_command = new NodeSetPositionAndShiftSurroundingsCommand(surrounding, new_pos, true);
sur_command->redo();
commands_.append(sur_command);
}
}
} else {
for (int i=0; i<commands_.size(); i++) {
commands_.at(i)->redo();
commands_.at(i)->redo_now();
}
}
}
void NodeSetPositionCommand::redo()
{
graph_ = node_->parent();
if (!(added_ = !graph_->NodeMapContainsNode(node_, relevant_))) {
old_pos_ = graph_->GetNodePosition(node_, relevant_);
}
graph_->SetNodePosition(node_, relevant_, pos_);
}
void NodeSetPositionCommand::undo()
{
if (added_) {
graph_->RemoveNodePosition(node_, relevant_);
} else {
graph_->SetNodePosition(node_, relevant_, 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();
}
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_);
if (contained_) {
old_pos_ = graph->GetNodePosition(node_, context_);
graph->RemoveNodePosition(node_, context_);
}
}
void NodeRemovePositionFromContextCommand::undo()
{
if (contained_) {
NodeGraph *graph = node_->parent();
graph->SetNodePosition(node_, context_, old_pos_);
}
}
void NodeRemovePositionFromAllContextsCommand::redo()
{
NodeGraph *graph = node_->parent();
if (points_.empty()) {
// No points yet, let's see what points we should remove
auto map = graph->GetPositionMap();
for (auto it=map.cbegin(); it!=map.cend(); it++) {
if (it.value().contains(node_)) {
points_.insert({it.key(), it.value().value(node_)});
}
}
}
for (auto it=points_.cbegin(); it!=points_.cend(); it++) {
graph->RemoveNodePosition(node_, it->first);
}
}
void NodeRemovePositionFromAllContextsCommand::undo()
{
NodeGraph *graph = node_->parent();
for (auto it=points_.crbegin(); it!=points_.crend(); it++) {
graph->SetNodePosition(node_, it->first, it->second);
}
}
}
+125 -81
View File
@@ -23,6 +23,7 @@
#include <map>
#include <QCryptographicHash>
#include <QMutex>
#include <QObject>
#include <QPainter>
#include <QPointF>
@@ -557,7 +558,8 @@ public:
* Whether to keep traversing down outputs to find this node (TRUE) or stick to immediate outputs
* (FALSE).
*/
bool OutputsTo(Node* n, bool recursively) const;
bool OutputsTo(Node* n, bool recursively, const OutputConnections &ignore_edges = OutputConnections(), const OutputConnection &added_edge = OutputConnection()) const;
/**
* @brief Same as OutputsTo(Node*), but for a node ID rather than a specific instance.
*/
@@ -581,7 +583,7 @@ public:
/**
* @brief Determines how many paths go from this node out to another node
*/
int GetRoutesTo(Node* n) const;
int GetNumberOfRoutesTo(Node* n) const;
/**
* @brief Severs all input and output connections
@@ -621,6 +623,8 @@ public:
*/
static T* ValueToPtr(const QVariant& ptr);
using InvalidateCacheOptions = QHash<QString, QVariant>;
/**
* @brief Signal all dependent Nodes that anything cached between start_range and end_range is now invalid and
* requires re-rendering
@@ -630,16 +634,11 @@ public:
* the DAG. Even if the time needs to be transformed somehow (e.g. converting media time to sequence time), you can
* call this function with transformed time and relay the signal that way.
*/
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time);
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1, InvalidateCacheOptions options = InvalidateCacheOptions());
void InvalidateCache(const TimeRange& range, const QString& from, int element = -1)
void InvalidateCache(const TimeRange& range, const NodeInput& from, const InvalidateCacheOptions &options = InvalidateCacheOptions())
{
InvalidateCache(range, from, element, last_change_time_);
}
void InvalidateCache(const TimeRange& range, const NodeInput& from)
{
InvalidateCache(range, from.input(), from.element());
InvalidateCache(range, from.input(), from.element(), options);
}
/**
@@ -689,9 +688,9 @@ public:
static QVector<Node*> CopyDependencyGraph(const QVector<Node*>& nodes, MultiUndoCommand *command);
static void CopyDependencyGraph(const QVector<Node*>& src, const QVector<Node*>& dst, MultiUndoCommand *command);
static Node* CopyNodeAndDependencyGraphMinusItems(const Node* node, MultiUndoCommand* command);
static Node* CopyNodeAndDependencyGraphMinusItems(Node* node, MultiUndoCommand* command);
static Node* CopyNodeInGraph(const Node* node, MultiUndoCommand* command);
static Node* CopyNodeInGraph(Node *node, MultiUndoCommand* command);
/**
* @brief Return whether this Node can be deleted or not
@@ -718,10 +717,6 @@ public:
*/
virtual NodeValueTable Value(const QString &output, NodeValueDatabase& value) const;
const QPointF& GetPosition() const;
void SetPosition(const QPointF& pos, bool move_dependencies_relatively_too = false);
virtual bool HasGizmos() const;
virtual void DrawGizmos(NodeValueDatabase& db, QPainter* p);
@@ -889,7 +884,7 @@ protected:
SetInputProperty(id, QStringLiteral("combo_str"), strings);
}
void SendInvalidateCache(const TimeRange &range, qint64 job_time);
void SendInvalidateCache(const TimeRange &range, const InvalidateCacheOptions &options);
/**
* @brief Don't send cache invalidation signals if `input` is connected or disconnected
@@ -1014,6 +1009,7 @@ private:
virtual Project* GetRelevantProject() const override;
protected:
virtual void redo() override
{
node_->InputArrayInsert(input_, index_, false);
@@ -1040,6 +1036,9 @@ private:
size_(size)
{}
virtual Project* GetRelevantProject() const override;
protected:
virtual void redo() override
{
old_size_ = node_->InputArraySize(input_);
@@ -1072,8 +1071,6 @@ private:
node_->ArrayResizeInternal(input_, old_size_);
}
virtual Project* GetRelevantProject() const override;
private:
Node* node_;
QString input_;
@@ -1130,7 +1127,7 @@ private:
void ArrayResizeInternal(const QString& id, int size);
static Node *CopyNodeAndDependencyGraphMinusItemsInternal(QMap<const Node*, Node*>& created, const Node *node, MultiUndoCommand *command);
static Node *CopyNodeAndDependencyGraphMinusItemsInternal(QMap<Node *, Node *> &created, Node *node, MultiUndoCommand *command);
/**
* @brief Immediates aren't deleted, so the actual array size may be larger than ArraySize()
@@ -1157,8 +1154,6 @@ private:
void SaveImmediate(QXmlStreamWriter *writer, const QString &input, int element) const;
void UpdateLastChangedTime();
/**
* @brief Intelligently determine how what time range is affected by a keyframe
*/
@@ -1180,11 +1175,6 @@ private:
*/
bool can_be_deleted_;
/**
* @brief UI position for NodeViews
*/
QPointF position_;
/**
* @brief Custom user label for node
*/
@@ -1213,8 +1203,6 @@ private:
OutputConnections output_connections_;
qint64 last_change_time_;
QString tooltip_;
Folder* folder_;
@@ -1312,44 +1300,41 @@ using NodePtr = std::shared_ptr<Node>;
class NodeSetPositionCommand : public UndoCommand
{
public:
NodeSetPositionCommand(Node* node, const QPointF& position, bool move_dependencies_relatively) :
node_(node),
new_pos_(position),
move_deps_(move_dependencies_relatively)
NodeSetPositionCommand(Node* node, Node* relevant, const QPointF& pos, bool move_dependencies_relatively)
{
node_ = node;
relevant_ = relevant;
pos_ = pos;
move_deps_ = move_dependencies_relatively;
}
virtual Project * GetRelevantProject() const override
virtual Project* GetRelevantProject() const override
{
return node_->project();
}
virtual void redo() override
{
old_pos_ = node_->GetPosition();
node_->SetPosition(new_pos_, move_deps_);
}
protected:
virtual void redo() override;
virtual void undo() override
{
node_->SetPosition(old_pos_, move_deps_);
}
virtual void undo() override;
private:
Node* node_;
QPointF new_pos_;
Node* relevant_;
QPointF pos_;
QPointF old_pos_;
bool added_;
bool move_deps_;
NodeGraph *graph_;
};
class NodeSetPositionAndShiftSurroundingsCommand : public UndoCommand
{
public:
NodeSetPositionAndShiftSurroundingsCommand(Node* node, const QPointF& pos, bool move_dependencies_relatively) :
NodeSetPositionAndShiftSurroundingsCommand(Node* node, Node *relative, const QPointF& pos, bool move_dependencies_relatively) :
node_(node),
relative_(relative),
position_(pos),
move_dependencies_(move_dependencies_relatively)
{}
@@ -1364,18 +1349,21 @@ public:
return node_->project();
}
protected:
virtual void redo() override;
virtual void undo() override
{
for (int i=commands_.size()-1; i>=0; i--) {
commands_.at(i)->undo();
commands_.at(i)->undo_now();
}
}
private:
Node* node_;
Node *relative_;
QPointF position_;
bool move_dependencies_;
@@ -1387,9 +1375,10 @@ private:
class NodeSetPositionAsChildCommand : public UndoCommand
{
public:
NodeSetPositionAsChildCommand(Node* node, Node* parent, int this_index, int child_count, bool shift_surroundings) :
NodeSetPositionAsChildCommand(Node* node, Node* parent, Node *relative, double this_index, int child_count, bool shift_surroundings) :
node_(node),
parent_(parent),
relative_(relative),
this_index_(this_index),
child_count_(child_count),
shift_surroundings_(shift_surroundings),
@@ -1407,27 +1396,8 @@ public:
return node_->project();
}
virtual void redo() override
{
if (!sub_command_) {
// Calculate position of node
QPointF pos = parent_->GetPosition();
// 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);
if (shift_surroundings_) {
sub_command_ = new NodeSetPositionAndShiftSurroundingsCommand(node_, pos, true);
} else {
sub_command_ = new NodeSetPositionCommand(node_, pos, true);
}
}
sub_command_->redo();
}
protected:
virtual void redo() override;
virtual void undo() override
{
@@ -1437,22 +1407,44 @@ public:
private:
Node* node_;
Node* parent_;
Node *relative_;
int this_index_;
double this_index_;
int child_count_;
bool shift_surroundings_;
UndoCommand* sub_command_;
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, const QPointF& offset) :
NodeSetPositionToOffsetOfAnotherNodeCommand(Node* node, Node* other_node, Node *relative, const QPointF& offset) :
node_(node),
other_node_(other_node),
relative_(relative),
offset_(offset)
{}
@@ -1461,20 +1453,72 @@ public:
return node_->project();
}
virtual void redo() override
{
node_->SetPosition(other_node_->GetPosition() + offset_);
}
protected:
virtual void redo() override;
virtual void undo() override
{
node_->SetPosition(other_node_->GetPosition() - offset_);
}
virtual void undo() override;
private:
Node* node_;
Node* other_node_;
Node *relative_;
QPointF offset_;
QPointF old_pos_;
};
class NodeRemovePositionFromContextCommand : public UndoCommand
{
public:
NodeRemovePositionFromContextCommand(Node *node, Node *context) :
node_(node),
context_(context)
{
}
virtual Project * GetRelevantProject() const override
{
return node_->project();
}
protected:
virtual void redo() override;
virtual void undo() override;
private:
Node *node_;
Node *context_;
QPointF old_pos_;
bool contained_;
};
class NodeRemovePositionFromAllContextsCommand : public UndoCommand
{
public:
NodeRemovePositionFromAllContextsCommand(Node *node) :
node_(node)
{
}
virtual Project * GetRelevantProject() const override
{
return node_->project();
}
protected:
virtual void redo() override;
virtual void undo() override;
private:
Node *node_;
std::map<Node *, QPointF> points_;
};
+19 -62
View File
@@ -40,9 +40,6 @@ const QString Track::kMutedInput = QStringLiteral("muted_in");
Track::Track() :
track_type_(Track::kNone),
track_length_(0),
midop_track_length_(0),
preop_track_length_(0),
index_(-1),
locked_(false)
{
@@ -280,11 +277,8 @@ void Track::InputDisconnectedEvent(const QString &input, int element, const Node
// Update lengths
if (next) {
UpdateInOutFrom(blocks_.indexOf(next));
} else if (blocks_.isEmpty()) {
SetLengthInternal(0);
} else {
SetLengthInternal(blocks_.last()->out());
}
emit TrackLengthChanged();
disconnect(b, &Block::LengthChanged, this, &Track::BlockLengthChanged);
@@ -431,7 +425,7 @@ QVector<Block *> Track::BlocksAtTimeRange(const TimeRange &range) const
return list;
}
void Track::InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time)
void Track::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options)
{
if (GetOperationStack() != 0) {
return;
@@ -443,7 +437,8 @@ void Track::InvalidateCache(const TimeRange& range, const QString& from, int ele
if (from == kBlockInput
&& element >= 0
&& (b = dynamic_cast<const Block*>(GetConnectedOutput(from, element).node()))) {
&& (b = dynamic_cast<const Block*>(GetConnectedOutput(from, element).node()))
&& !options.value(QStringLiteral("lengthevent")).toBool()) {
// Limit the range signal to the corresponding block
if (range.out() <= b->in() || range.in() >= b->out()) {
return;
@@ -451,11 +446,14 @@ void Track::InvalidateCache(const TimeRange& range, const QString& from, int ele
limited = TimeRange(qMax(range.in(), b->in()), qMin(range.out(), b->out()));
} else {
limited = TimeRange(qMax(range.in(), rational(0)), qMin(range.out(), qMax(preop_track_length_, track_length())));
preop_track_length_ = track_length_;
limited = range;
}
Node::InvalidateCache(limited, from, element, job_time);
// NOTE: For now, I figure we drop this key, but we may find in the future that it's advantageous
// to keep it
options.remove(QStringLiteral("lengthevent"));
Node::InvalidateCache(limited, from, element, options);
}
void Track::InsertBlockBefore(Block* block, Block* after)
@@ -520,7 +518,7 @@ void Track::AppendBlock(Block *block)
EndOperation();
// Invalidate area that block was added to
Node::InvalidateCache(TimeRange(block->in(), track_length()), kBlockInput);
Node::InvalidateCache(TimeRange(block->in(), block->out()), kBlockInput);
}
void Track::RippleRemoveBlock(Block *block)
@@ -552,13 +550,17 @@ void Track::ReplaceBlock(Block *old, Block *replace)
if (old->length() == replace->length()) {
Node::InvalidateCache(TimeRange(replace->in(), replace->out()), kBlockInput);
} else {
Node::InvalidateCache(TimeRange(replace->in(), RATIONAL_MAX), kBlockInput);
Node::InvalidateCache(TimeRange(replace->in(), track_length()), kBlockInput);
}
}
const rational &Track::track_length() const
rational Track::track_length() const
{
return track_length_;
if (blocks_.isEmpty()) {
return 0;
} else {
return blocks_.last()->out();
}
}
QString Track::GetDefaultTrackName(Track::Type type, int index)
@@ -600,15 +602,6 @@ void Track::Hash(const QString &output, QCryptographicHash &hash, const rational
}
}
void Track::EndOperation()
{
super::EndOperation();
if (track_length_ != midop_track_length_) {
SetLengthInternal(midop_track_length_);
}
}
void Track::SetMuted(bool e)
{
SetStandardValue(kMutedInput, e);
@@ -640,7 +633,7 @@ void Track::UpdateInOutFrom(int index)
emit BlocksRefreshed();
// Update track length
SetLengthInternal(last_out);
emit TrackLengthChanged();
}
int Track::GetArrayIndexFromBlock(Block *block) const
@@ -658,48 +651,12 @@ int Track::GetCacheIndexFromArrayIndex(int index) const
return block_array_indexes_.indexOf(index);
}
void Track::SetLengthInternal(const rational &r, bool invalidate)
{
// Hold track length until operation stack is empty
midop_track_length_ = r;
if (GetOperationStack() == 0 && track_length_ != r) {
TimeRange invalidate_range(track_length_, r);
track_length_ = r;
preop_track_length_ = qMax(preop_track_length_, track_length_);
emit TrackLengthChanged();
if (invalidate) {
Node::InvalidateCache(invalidate_range, kBlockInput);
}
}
}
void Track::BlockLengthChanged()
{
// Assumes sender is a Block
Block* b = static_cast<Block*>(sender());
rational old_out = b->out();
UpdateInOutFrom(blocks_.indexOf(b));
rational new_out = b->out();
TimeRange invalidate_region(qMin(old_out, new_out), track_length());
// The cache won't start while dragging, so we store up our invalidations if it's held down
// and release them once the mouse is no longer pressed
if (qApp->mouseButtons() & Qt::LeftButton) {
block_length_pending_invalidations_.insert(invalidate_region);
} else if (!block_length_pending_invalidations_.isEmpty()) {
foreach (const TimeRange& r, block_length_pending_invalidations_) {
Node::InvalidateCache(r, kBlockInput);
}
block_length_pending_invalidations_.clear();
}
Node::InvalidateCache(invalidate_region, kBlockInput);
}
uint qHash(const Track::Reference &r, uint seed)
+2 -12
View File
@@ -286,7 +286,7 @@ public:
return blocks_;
}
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time) override;
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) override;
/**
* @brief Adds Block `block` at the very beginning of the Sequence before all other clips
@@ -330,7 +330,7 @@ public:
*/
void ReplaceBlock(Block* old, Block* replace);
const rational& track_length() const;
rational track_length() const;
static QString GetDefaultTrackName(Track::Type type, int index);
@@ -345,8 +345,6 @@ public:
return waveform_;
}
virtual void EndOperation() override;
static const double kTrackHeightDefault;
static const double kTrackHeightMinimum;
static const double kTrackHeightInterval;
@@ -420,8 +418,6 @@ private:
int GetCacheIndexFromArrayIndex(int index) const;
void SetLengthInternal(const rational& r, bool invalidate = true);
TimeRangeList block_length_pending_invalidations_;
QVector<Block*> blocks_;
@@ -429,12 +425,6 @@ private:
Track::Type track_type_;
rational track_length_;
rational midop_track_length_;
rational preop_track_length_;
double track_height_;
int index_;
+4 -10
View File
@@ -222,7 +222,7 @@ void ViewerOutput::ShiftCache(const rational &from, const rational &to)
ShiftAudioCache(from, to);
}
void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time)
void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options)
{
Q_UNUSED(element)
@@ -233,16 +233,16 @@ void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from,
if (invalidated_range.in() != invalidated_range.out()) {
if (from == kTextureInput || from == kVideoParamsInput) {
video_frame_cache_.Invalidate(invalidated_range, job_time);
video_frame_cache_.Invalidate(invalidated_range);
} else {
audio_playback_cache_.Invalidate(invalidated_range, job_time);
audio_playback_cache_.Invalidate(invalidated_range);
}
}
}
VerifyLength();
super::InvalidateCache(range, from, element, job_time);
super::InvalidateCache(range, from, element, options);
}
QVector<QString> ViewerOutput::inputs_for_output(const QString &output) const
@@ -300,14 +300,8 @@ void ViewerOutput::Retranslate()
void ViewerOutput::VerifyLength()
{
video_length_ = VerifyLengthInternal(Track::kVideo);
if (video_cache_enabled_) {
video_frame_cache_.SetLength(video_length_);
}
audio_length_ = VerifyLengthInternal(Track::kAudio);
if (audio_cache_enabled_) {
audio_playback_cache_.SetLength(audio_length_);
}
rational subtitle_length = VerifyLengthInternal(Track::kSubtitle);
+4 -4
View File
@@ -66,7 +66,7 @@ public:
void ShiftAudioCache(const rational& from, const rational& to);
void ShiftCache(const rational& from, const rational& to);
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time) override;
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) override;
virtual QVector<QString> inputs_for_output(const QString& output) const override;
@@ -154,6 +154,9 @@ public:
virtual NodeOutput GetConnectedSampleOutput();
void SetViewerVideoCacheEnabled(bool e) { video_cache_enabled_ = e; }
void SetViewerAudioCacheEnabled(bool e) { audio_cache_enabled_ = e; }
static const QString kVideoParamsInput;
static const QString kAudioParamsInput;
@@ -202,9 +205,6 @@ protected:
int AddStream(Track::Type type, const QVariant &value);
void SetViewerVideoCacheEnabled(bool e) { video_cache_enabled_ = e; }
void SetViewerAudioCacheEnabled(bool e) { audio_cache_enabled_ = e; }
private:
rational last_length_;
rational video_length_;
+3 -4
View File
@@ -138,18 +138,17 @@ void FolderAddChild::redo()
Node::ConnectEdge(child_, NodeInput(folder_, Folder::kChildInput, array_index));
if (autoposition_) {
old_position_ = child_->GetPosition();
if (!position_command_) {
position_command_ = new NodeSetPositionAsChildCommand(child_, folder_, array_index, array_index+1, true);
position_command_ = new NodeSetPositionAsChildCommand(child_, folder_, folder_->project()->root(), array_index, array_index+1, true);
}
position_command_->redo();
position_command_->redo_now();
}
}
void FolderAddChild::undo()
{
if (position_command_) {
position_command_->undo();
position_command_->undo_now();
}
Node::DisconnectEdge(child_, NodeInput(folder_, Folder::kChildInput, folder_->InputArraySize(Folder::kChildInput)-1));
+2 -2
View File
@@ -135,6 +135,7 @@ public:
return folder_->project();
}
protected:
virtual void redo() override;
virtual void undo() override
@@ -209,6 +210,7 @@ public:
virtual Project * GetRelevantProject() const override;
protected:
virtual void redo() override;
virtual void undo() override;
@@ -220,8 +222,6 @@ private:
bool autoposition_;
QPointF old_position_;
NodeSetPositionAsChildCommand* position_command_;
};
+100 -8
View File
@@ -39,26 +39,27 @@ Project::Project() :
// Generate UUID for this project
RegenerateUuid();
// Folder root for project
root_ = new Folder();
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);
color_manager_->SetPosition(QPointF(1, 0));
SetNodePosition(color_manager_, root_, QPointF(1, 0));
color_manager_->SetCanBeDeleted(false);
AddDefaultNode(color_manager_);
// Same with project settings
settings_ = new ProjectSettingsNode();
settings_->setParent(this);
settings_->SetPosition(QPointF(2, 0));
SetNodePosition(settings_, root_, QPointF(2, 0));
settings_->SetCanBeDeleted(false);
AddDefaultNode(settings_);
// Folder root for project
root_ = new Folder();
root_->setParent(this);
root_->SetLabel(tr("Root"));
root_->SetCanBeDeleted(false);
connect(color_manager(), &ColorManager::ValueChanged,
this, &Project::ColorManagerValueChanged);
}
@@ -135,6 +136,71 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
}
}
} else if (reader->name() == QStringLiteral("positions")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("context")) {
quintptr context_ptr = 0;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("ptr")) {
context_ptr = attr.value().toULongLong();
break;
}
}
Node *context = xml_node_data.node_ptrs.value(context_ptr);
if (!context) {
qWarning() << "Failed to find pointer for context";
reader->skipCurrentElement();
} else {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
quintptr node_ptr = 0;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("ptr")) {
node_ptr = attr.value().toULongLong();
break;
}
}
Node *node = xml_node_data.node_ptrs.value(node_ptr);
if (!node) {
qWarning() << "Failed to find pointer for node position";
reader->skipCurrentElement();
} else {
QPointF pos;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("x")) {
pos.setX(reader->readElementText().toDouble());
} else if (reader->name() == QStringLiteral("y")) {
pos.setY(reader->readElementText().toDouble());
} else {
reader->skipCurrentElement();
}
}
SetNodePosition(node, context, pos);
}
} else {
reader->skipCurrentElement();
}
}
}
} else {
reader->skipCurrentElement();
}
}
} else {
// Skip this
@@ -176,6 +242,32 @@ void Project::Save(QXmlStreamWriter *writer) const
writer->writeEndElement(); // nodes
writer->writeStartElement(QStringLiteral("positions"));
for (auto it=GetPositionMap().cbegin(); it!=GetPositionMap().cend(); it++) {
writer->writeStartElement(QStringLiteral("context"));
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(it.key())));
const PositionMap &map = it.value();
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
writer->writeStartElement(QStringLiteral("node"));
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(jt.key())));
const QPointF &pos = jt.value();
writer->writeTextElement(QStringLiteral("x"), QString::number(pos.x()));
writer->writeTextElement(QStringLiteral("y"), QString::number(pos.y()));
writer->writeEndElement(); // node
}
writer->writeEndElement(); // context
}
writer->writeEndElement(); // positions
// Save main window project layout
MainWindowLayoutInfo main_window_info = Core::instance()->main_window()->SaveLayout();
main_window_info.toXml(writer);
+3 -2
View File
@@ -24,6 +24,7 @@
#include "panel/timeline/timeline.h"
#include "ui/icons/icons.h"
#include "widget/timelinewidget/undo/timelineundogeneral.h"
namespace olive {
@@ -63,8 +64,8 @@ void Sequence::add_default_nodes(MultiUndoCommand* command)
command->add_child(video_track_command);
command->add_child(audio_track_command);
} else {
video_track_command->redo();
audio_track_command->redo();
video_track_command->redo_now();
audio_track_command->redo_now();
delete video_track_command;
delete audio_track_command;
}