added some more timeline functions

This commit is contained in:
itsmattkc
2019-08-13 21:50:39 +10:00
parent 737442e513
commit 4f4737a395
21 changed files with 291 additions and 87 deletions
+8 -1
View File
@@ -59,9 +59,16 @@ const rational &Block::out()
return out_point_;
}
const rational& Block::length()
{
return length_;
}
void Block::set_length(const rational &length)
{
Q_UNUSED(length)
length_ = length;
RefreshFollowing();
}
Block *Block::previous()
+6 -2
View File
@@ -41,6 +41,8 @@ public:
kEnd
};
virtual Block* copy() = 0;
virtual Type type() = 0;
virtual QString Category() override;
@@ -48,8 +50,8 @@ public:
const rational& in();
const rational& out();
virtual rational length() = 0;
virtual void set_length(const rational& length);
virtual const rational &length();
virtual void set_length(const rational &length);
virtual Block* previous();
virtual Block* next();
@@ -106,6 +108,8 @@ private:
rational in_point_;
rational out_point_;
rational length_;
private slots:
void BlockOrderChanged(NodeEdgePtr edge);
+13 -12
View File
@@ -29,6 +29,19 @@ ClipBlock::ClipBlock()
AddParameter(texture_input_);
}
Block *ClipBlock::copy()
{
ClipBlock* c = new ClipBlock();
// Duplicate connection
// FIXME: This behavior should probably be configurable
if (texture_input()->IsConnected()) {
NodeParam::ConnectEdge(texture_input()->edges().first()->output(), c->texture_input());
}
return new ClipBlock();
}
Block::Type ClipBlock::type()
{
return kClip;
@@ -49,18 +62,6 @@ QString ClipBlock::Description()
return tr("A time-based node that represents a media source.");
}
rational ClipBlock::length()
{
return length_;
}
void ClipBlock::set_length(const rational &length)
{
length_ = length;
RefreshFollowing();
}
NodeInput *ClipBlock::texture_input()
{
return texture_input_;
+2 -4
View File
@@ -32,15 +32,14 @@ class ClipBlock : public Block
public:
ClipBlock();
virtual Block* copy() override;
virtual Type type() override;
virtual QString Name() override;
virtual QString id() override;
virtual QString Description() override;
virtual rational length() override;
virtual void set_length(const rational &length) override;
NodeInput* texture_input();
public slots:
@@ -51,7 +50,6 @@ private:
rational media_in_;
rational length_;
};
#endif // TIMELINEBLOCK_H
+5 -12
View File
@@ -24,6 +24,11 @@ GapBlock::GapBlock()
{
}
Block *GapBlock::copy()
{
return new GapBlock();
}
Block::Type GapBlock::type()
{
return kGap;
@@ -43,15 +48,3 @@ QString GapBlock::Description()
{
return tr("A time-based node that represents an empty space.");
}
rational GapBlock::length()
{
return length_;
}
void GapBlock::set_length(const rational &length)
{
length_ = length;
RefreshFollowing();
}
+3 -4
View File
@@ -32,17 +32,16 @@ class GapBlock : public Block
public:
GapBlock();
virtual Block * copy() override;
virtual Type type() override;
virtual QString Name() override;
virtual QString id() override;
virtual QString Description() override;
virtual rational length() override;
virtual void set_length(const rational &length) override;
private:
rational length_;
};
#endif // TIMELINEBLOCK_H
+17 -2
View File
@@ -53,13 +53,13 @@ void NodeGraph::AddNodeWithDependencies(Node *node)
}
}
void NodeGraph::RemoveNode(Node *node)
void NodeGraph::TakeNode(Node *node, QObject* new_parent)
{
if (!ContainsNode(node)) {
return;
}
node->setParent(nullptr);
node->setParent(new_parent);
disconnect(node, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SIGNAL(EdgeAdded(NodeEdgePtr)));
disconnect(node, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SIGNAL(EdgeRemoved(NodeEdgePtr)));
@@ -67,6 +67,21 @@ void NodeGraph::RemoveNode(Node *node)
emit NodeRemoved(node);
}
QList<Node *> NodeGraph::TakeNodeWithItsDependencies(Node *node, QObject *new_parent)
{
if (!ContainsNode(node)) {
return QList<Node*>();
}
QList<Node*> deps = node->GetExclusiveDependencies();
foreach (Node* d, deps) {
TakeNode(d, new_parent);
}
return deps;
}
QList<Node *> NodeGraph::nodes()
{
return static_qobjectlist_cast<Node>(children());
+15 -3
View File
@@ -49,14 +49,26 @@ public:
* @brief Adds a node to this graph and all nodes connected to its inputs
*
* Adds the Node to the graph and runs through its inputs adding all of its dependencies (and all of their
* dependencies and so forth).
* dependencies and so forth). The graph takes ownershi of all Nodes added through this process.
*/
void AddNodeWithDependencies(Node* node);
/**
* @brief Removes a Node from the graph BUT doesn't destroy it. Ownership is returned to the caller.
* @brief Removes a Node from the graph BUT doesn't destroy it. Ownership is passed to `new_parent`.
*/
void TakeNode(Node* node);
void TakeNode(Node* node, QObject* new_parent = nullptr);
/**
* @brief Removes a Node from the graph and its dependencies (ONLY if the dependencies are exclusive to this Node).
*
* Returns a list of all Nodes that were removed in this process (except the Node used as a parameter)
*
* Only dependencies that are exclusively dependencies of this Node are removed. If a dependency Node is also
* used as the dependency of another Node, it is not removed and not returned in the list.
*
* Ownership of all Nodes is passed to `new_parent`.
*/
QList<Node*> TakeNodeWithItsDependencies(Node* node, QObject* new_parent = nullptr);
/**
* @brief Retrieve a complete list of the nodes belonging to this graph
+35
View File
@@ -119,6 +119,41 @@ QList<Node *> Node::GetDependencies()
return node_list;
}
QList<Node *> Node::GetExclusiveDependencies()
{
QList<Node*> deps = GetDependencies();
// Filter out any dependencies that are used elsewhere
for (int i=0;i<deps.size();i++) {
QList<NodeParam*> params = deps.at(i)->parameters();
// See if any of this Node's outputs are used outside of this dep list
for (int j=0;j<params.size();j++) {
NodeParam* p = params.at(j);
if (p->type() == NodeParam::kOutput) {
QVector<NodeEdgePtr> edges = p->edges();
for (int k=0;k<edges.size();k++) {
NodeEdgePtr edge = edges.at(k);
// If any edge goes to from an output here to an input of a Node that isn't in this dep list, it's NOT an
// exclusive dependency
if (deps.contains(edge->input()->parent())) {
deps.removeAt(i);
i--; // -1 since we just removed a Node here
j = params.size(); // No need to keep looking at this Node's params
break; // Or this param's edges
}
}
}
}
}
return deps;
}
QVariant Node::PtrToValue(void *ptr)
{
return reinterpret_cast<quintptr>(ptr);
+8
View File
@@ -113,6 +113,14 @@ public:
*/
QList<Node*> GetDependencies();
/**
* @brief Returns a list of Nodes that this Node is dependent on, provided no other Nodes are dependent on them
* outside of this hierarchy.
*
* Similar to GetDependencies(), but excludes any Nodes that are used outside the dependency graph of this Node.
*/
QList<Node*> GetExclusiveDependencies();
/**
* @brief Convert a pointer to a value that can be sent between NodeParams
*/
+93 -18
View File
@@ -26,7 +26,6 @@
#include "node/graph.h"
TimelineOutput::TimelineOutput() :
first_block_(nullptr),
current_block_(this),
attached_timeline_(nullptr)
{
@@ -37,6 +36,11 @@ Block::Type TimelineOutput::type()
return kEnd;
}
Block *TimelineOutput::copy()
{
return new TimelineOutput();
}
QString TimelineOutput::Name()
{
return tr("Timeline");
@@ -89,14 +93,16 @@ void TimelineOutput::AttachTimeline(TimelinePanel *timeline)
previous_block = previous_block->previous();
}
connect(attached_timeline_, SIGNAL(RequestInsertBlockAtIndex(Block*, int)), this, SLOT(InsertBlockAtIndex(Block*, int)));
connect(attached_timeline_, SIGNAL(RequestPlaceBlock(Block*, rational)), this, SLOT(PlaceBlock(Block*, rational)));
TimelineView* view = attached_timeline_->view();
connect(view, SIGNAL(RequestInsertBlockAtIndex(Block*, int)), this, SLOT(InsertBlockAtIndex(Block*, int)));
connect(view, SIGNAL(RequestPlaceBlock(Block*, rational)), this, SLOT(PlaceBlock(Block*, rational)));
}
}
rational TimelineOutput::length()
void TimelineOutput::set_length(const rational &)
{
return 0;
// Prevent length changing on this Block
}
void TimelineOutput::Refresh()
@@ -167,13 +173,9 @@ void TimelineOutput::InsertBlockBetweenBlocks(Block *block, Block *before, Block
Block::ConnectBlocks(block, after);
}
Block *TimelineOutput::first_block()
void TimelineOutput::InsertBlockAfter(Block *block, Block *before)
{
if (block_cache_.isEmpty()) {
return nullptr;
}
return block_cache_.first();
InsertBlockBetweenBlocks(block, before, before->next());
}
Block *TimelineOutput::attached_block()
@@ -248,6 +250,7 @@ void TimelineOutput::PlaceBlock(Block *block, rational start)
{
AddBlockToGraph(block);
// Place block at the beginning
if (start == 0) {
// FIXME: Remove existing
@@ -256,15 +259,16 @@ void TimelineOutput::PlaceBlock(Block *block, rational start)
}
// Check if the placement location is past the end of the timeline
if (start > in()) {
// FIXME: Remove existing
if (start >= in()) {
if (start > in()) {
// If so, insert a gap here
GapBlock* gap = new GapBlock();
gap->set_length(start - in());
// If so, insert a gap here
GapBlock* gap = new GapBlock();
gap->set_length(start - in());
// Then append them
AppendBlock(gap);
}
// Then append them
AppendBlock(gap);
AppendBlock(block);
return;
@@ -284,3 +288,74 @@ void TimelineOutput::PlaceBlock(Block *block, rational start)
}
}
}
void TimelineOutput::RemoveBlock(Block *block)
{
GapBlock* gap = new GapBlock();
gap->set_length(block->length());
Block* previous = block->previous();
Block* next = block->next();
// Remove block
RippleRemoveBlock(block);
if (previous == nullptr) {
// Block must be at the beginning
PrependBlock(gap);
} else {
InsertBlockBetweenBlocks(gap, previous, next);
}
}
void TimelineOutput::RippleRemoveBlock(Block *block)
{
Block* previous = block->previous();
Block* next = block->next();
if (previous != nullptr) {
Block::DisconnectBlocks(previous, block);
}
if (next != nullptr) {
Block::DisconnectBlocks(block, next);
}
if (previous != nullptr && next != nullptr) {
Block::ConnectBlocks(previous, next);
}
}
void TimelineOutput::SplitBlock(Block *block, rational time)
{
if (time < block->in() || time >= block->out()) {
return;
}
rational original_length = block->length();
block->set_length(time - block->in());
Block* copy = block->copy();
copy->set_length(original_length - block->length());
InsertBlockAfter(copy, block);
}
void TimelineOutput::SpliceBlock(Block *inner, Block *outer, rational inner_in)
{
Q_ASSERT(inner_in >= outer->in() && inner_in < outer->out());
// Cache original length
rational original_length = outer->length();
// Set outer clip to the clip that PRECEDES the inner clip
outer->set_length(inner_in - outer->in());
// Insert inner clip between BEFORE clip and its next clip
InsertBlockAfter(inner, outer);
// Create the AFTER clip
Block* copy = outer->copy();
copy->set_length(original_length - outer->length() - inner->length());
InsertBlockAfter(copy, inner);
}
+17 -10
View File
@@ -35,6 +35,8 @@ public:
virtual Type type() override;
virtual Block* copy() override;
virtual QString Name() override;
virtual QString id() override;
virtual QString Category() override;
@@ -42,7 +44,7 @@ public:
void AttachTimeline(TimelinePanel* timeline);
virtual rational length() override;
virtual void set_length(const rational &length) override;
virtual void Refresh() override;
@@ -67,13 +69,10 @@ private:
*/
void AddBlockToGraph(Block* block);
QVector<Block*> block_cache_;
Block* first_block();
Block* attached_block();
Block* first_block_;
QVector<Block*> block_cache_;
Block* current_block_;
TimelinePanel* attached_timeline_;
@@ -99,6 +98,13 @@ private slots:
*/
void InsertBlockBetweenBlocks(Block* block, Block* before, Block* after);
/**
* @brief Inserts Block after another Block
*
* Equivalent to calling InsertBlockBetweenBlocks(block, before, before->next())
*/
void InsertBlockAfter(Block* block, Block* before);
/**
* @brief Adds Block `block` at the very end of the Sequence after all other clips
*/
@@ -124,14 +130,15 @@ private slots:
void RippleRemoveBlock(Block* block);
/**
* @brief Removes the Block at the given index pushing all subsequent Blocks earlier to take up the space
* @brief Splits `block` into two Blocks at the Sequence point `time`
*/
void RippleRemoveBlockAtIndex(int index);
void SplitBlock(Block* block, rational time);
/**
* @brief Removes the last Block of the Sequence at the given index
* @brief Inserts Block `inner` between Block `outer`, splitting and shortening it to fit without changing the overall
* length
*/
void RippleRemoveLast();
void SpliceBlock(Block* inner, Block* outer, rational inner_in);
};
#endif // TIMELINEOUTPUT_H
+5
View File
@@ -57,6 +57,11 @@ int NodeParam::index()
return parent()->IndexOfParameter(this);
}
bool NodeParam::IsConnected()
{
return !edges_.isEmpty();
}
const QVector<NodeEdgePtr> &NodeParam::edges()
{
return edges_;
+5
View File
@@ -106,6 +106,11 @@ public:
*/
int index();
/**
* @brief Returns whether anything is connected to this parameter or not
*/
bool IsConnected();
/**
* @brief Return a list of edges (aka connections to other nodes)
*
-2
View File
@@ -37,8 +37,6 @@ TimelinePanel::TimelinePanel(QWidget *parent) :
layout->addWidget(ruler_);
view_ = new TimelineView(this);
connect(view_, SIGNAL(RequestInsertBlockAtIndex(Block*, int)), this, SIGNAL(RequestInsertBlockAtIndex(Block*, int)));
connect(view_, SIGNAL(RequestPlaceBlock(Block*, rational)), this, SIGNAL(RequestPlaceBlock(Block*, rational)));
layout->addWidget(view_);
connect(view_->horizontalScrollBar(), SIGNAL(valueChanged(int)), ruler_, SLOT(SetScroll(int)));
+3
View File
@@ -88,4 +88,7 @@ private:
using StreamPtr = std::shared_ptr<Stream>;
#include <QMetaType>
Q_DECLARE_METATYPE(StreamPtr)
#endif // STREAM_H
+5 -1
View File
@@ -198,7 +198,11 @@ void TimelineView::ClearGhosts()
void TimelineView::BlockChanged()
{
clip_items_[static_cast<Block*>(sender())]->UpdateRect();
TimelineViewRect* rect = clip_items_[static_cast<Block*>(sender())];
if (rect != nullptr) {
rect->UpdateRect();
}
}
TimelineView::Tool::Tool(TimelineView *parent) :
@@ -40,6 +40,16 @@ const rational &TimelineViewGhostItem::Out()
return out_;
}
rational TimelineViewGhostItem::Length()
{
return out_ - in_;
}
rational TimelineViewGhostItem::AdjustedLength()
{
return GetAdjustedOut() - GetAdjustedIn();
}
void TimelineViewGhostItem::SetIn(const rational &in)
{
in_ = in;
@@ -78,14 +88,14 @@ rational TimelineViewGhostItem::GetAdjustedOut()
return out_ + out_adj_;
}
StreamPtr TimelineViewGhostItem::stream()
const QVariant &TimelineViewGhostItem::data()
{
return stream_;
return data_;
}
void TimelineViewGhostItem::SetStream(StreamPtr f)
void TimelineViewGhostItem::SetData(const QVariant &data)
{
stream_ = f;
data_ = data;
}
void TimelineViewGhostItem::UpdateRect()
@@ -21,6 +21,8 @@
#ifndef TIMELINEVIEWGHOSTITEM_H
#define TIMELINEVIEWGHOSTITEM_H
#include <QVariant>
#include "project/item/footage/footage.h"
#include "timelineviewrect.h"
@@ -35,6 +37,9 @@ public:
const rational& In();
const rational& Out();
rational Length();
rational AdjustedLength();
void SetIn(const rational& in);
void SetOut(const rational& out);
@@ -44,8 +49,8 @@ public:
rational GetAdjustedIn();
rational GetAdjustedOut();
StreamPtr stream();
void SetStream(StreamPtr f);
const QVariant& data();
void SetData(const QVariant& data);
virtual void UpdateRect() override;
@@ -59,6 +64,8 @@ private:
rational out_adj_;
StreamPtr stream_;
QVariant data_;
};
#endif // TIMELINEVIEWGHOSTITEM_H
+20 -10
View File
@@ -47,6 +47,9 @@ void TimelineView::ImportTool::DragEnter(QDragEnterEvent *event)
quintptr item_ptr;
int r;
// Set ghosts to start where the cursor entered
rational ghost_start = parent()->ScreenToTime(event->pos().x());
while (!stream.atEnd()) {
stream >> r >> item_ptr;
@@ -65,14 +68,19 @@ void TimelineView::ImportTool::DragEnter(QDragEnterEvent *event)
rational footage_duration(stream->timebase().numerator() * stream->duration(),
stream->timebase().denominator());
ghost->SetIn(0);
ghost->SetOut(footage_duration);
ghost->SetStream(stream);
ghost->SetIn(ghost_start);
ghost->SetOut(ghost_start + footage_duration);
ghost->SetData(QVariant::fromValue(stream));
parent()->AddGhost(ghost);
// Stack each ghost one after the other
ghost_start += footage_duration;
}
}
drag_start_ = event->pos();
event->accept();
} else {
// FIXME: Implement dropping from file
@@ -85,10 +93,12 @@ void TimelineView::ImportTool::DragMove(QDragMoveEvent *event)
if (parent()->HasGhosts()) {
// Move ghosts to the mouse cursor
foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) {
rational time = parent()->ScreenToTime(event->pos().x());
QPoint movement = event->pos() - drag_start_;
ghost->SetOut(ghost->Out() - ghost->In() + time);
ghost->SetIn(time);
rational time_movement = parent()->ScreenToTime(movement.x());
ghost->SetInAdjustment(time_movement);
ghost->SetOutAdjustment(time_movement);
}
event->accept();
@@ -125,15 +135,15 @@ void TimelineView::ImportTool::DragDrop(QDropEvent *event)
clip->setParent(&node_memory_manager);
media->setParent(&node_memory_manager);
clip->set_length(ghost->Out() - ghost->In());
media->SetFootage(ghost->stream()->footage());
clip->set_length(ghost->Length());
media->SetFootage(ghost->data().value<StreamPtr>()->footage());
NodeParam::ConnectEdge(media->texture_output(), clip->texture_input());
if (event->keyboardModifiers() & Qt::ControlModifier) {
emit parent()->RequestPlaceBlock(clip, ghost->In());
emit parent()->RequestInsertBlockAtTime(clip, ghost->GetAdjustedIn());
} else {
emit parent()->RequestInsertBlockAtTime(clip, ghost->In());
emit parent()->RequestPlaceBlock(clip, ghost->GetAdjustedIn());
}
}
+8
View File
@@ -51,6 +51,7 @@ void TimelineView::PointerTool::MouseMove(QMouseEvent *event)
parent()->QGraphicsView::mouseMoveEvent(event);
if (!dragging_) {
// Let's see if there's anything selected to drag
if (parent()->itemAt(event->pos()) != nullptr) {
QList<QGraphicsItem*> selected_items = parent()->scene_.selectedItems();
@@ -76,6 +77,7 @@ void TimelineView::PointerTool::MouseMove(QMouseEvent *event)
}
dragging_ = true;
} else if (!parent()->ghost_items_.isEmpty()) {
QPoint movement = event->pos() - drag_start_;
@@ -103,6 +105,12 @@ void TimelineView::PointerTool::MouseRelease(QMouseEvent *event)
parent()->QGraphicsView::mouseReleaseEvent(event);
foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) {
Block* b = Node::ValueToPtr<Block>(ghost->data());
emit parent()->RequestPlaceBlock(b, ghost->In());
}
parent()->ClearGhosts();
dragging_ = false;