inserting blocks through the timeline implemented

This commit is contained in:
itsmattkc
2019-08-11 17:30:27 +10:00
parent 7174245a7e
commit 9b63cc7755
24 changed files with 284 additions and 58 deletions
+2 -5
View File
@@ -294,14 +294,11 @@ void Core::CreateNewSequence()
NodeParam::ConnectEdge(sg->texture_output(), cb1->texture_input());
NodeParam::ConnectEdge(ii->texture_output(), cb2->texture_input());
NodeParam::ConnectEdge(cb1->block_output(), cb2->previous_input());
NodeParam::ConnectEdge(cb2->block_output(), cb1->next_input());
Block::ConnectBlocks(cb1, cb2);
NodeParam::ConnectEdge(cb2->block_output(), tb->block_input());
NodeParam::ConnectEdge(tb->texture_output(), vo->texture_input());
cb1->Refresh();
cb2->Refresh();
tb->Refresh();
tb->AttachTimeline(olive::panel_focus_manager->MostRecentlyFocused<TimelinePanel>());
olive::panel_focus_manager->MostRecentlyFocused<NodePanel>()->SetGraph(new_sequence.get());
+21 -11
View File
@@ -88,7 +88,7 @@ void Block::Process(const rational &time)
}
void Block::Refresh()
{
{
// Set in point to the out point of the previous Node
if (previous() != nullptr) {
in_point_ = previous()->out();
@@ -98,22 +98,20 @@ void Block::Refresh()
// Update out point by adding this clip's length to the just calculated in point
out_point_ = in_point_ + length();
emit Refreshed();
}
void Block::RefreshSurrounds()
void Block::RefreshFollowing()
{
Block* acting_node = previous();
Refresh();
while (acting_node != nullptr) {
acting_node->Refresh();
acting_node = acting_node->previous();
}
Block* next_block = next();
acting_node = next();
while (next_block != nullptr) {
next_block->Refresh();
while (acting_node != nullptr) {
acting_node->Refresh();
acting_node = acting_node->next();
next_block = next_block->next();
}
}
@@ -126,3 +124,15 @@ NodeOutput *Block::block_output()
{
return block_output_;
}
void Block::ConnectBlocks(Block *previous, Block *next)
{
NodeParam::ConnectEdge(previous->block_output(), next->previous_input());
NodeParam::ConnectEdge(next->block_output(), previous->next_input());
}
void Block::DisconnectBlocks(Block *previous, Block *next)
{
NodeParam::DisconnectEdge(previous->block_output(), next->previous_input());
NodeParam::DisconnectEdge(next->block_output(), previous->next_input());
}
+15 -1
View File
@@ -35,6 +35,13 @@ class Block : public Node
public:
Block();
enum Type {
kClip,
kGap
};
virtual Type type() = 0;
virtual QString Category() override;
const rational& in();
@@ -52,6 +59,9 @@ public:
NodeOutput* texture_output();
NodeOutput* block_output();
static void ConnectBlocks(Block* previous, Block* next);
static void DisconnectBlocks(Block* previous, Block* next);
public slots:
virtual void Process(const rational &time) override;
@@ -70,8 +80,12 @@ public slots:
*/
void Refresh();
void RefreshFollowing();
signals:
void Refreshed();
protected:
void RefreshSurrounds();
private:
NodeInput* previous_input_;
+7
View File
@@ -29,6 +29,11 @@ ClipBlock::ClipBlock()
AddParameter(texture_input_);
}
Block::Type ClipBlock::type()
{
return kClip;
}
QString ClipBlock::Name()
{
return tr("Clip");
@@ -52,6 +57,8 @@ rational ClipBlock::length()
void ClipBlock::set_length(const rational &length)
{
length_ = length;
RefreshFollowing();
}
NodeInput *ClipBlock::texture_input()
+2
View File
@@ -32,6 +32,8 @@ class ClipBlock : public Block
public:
ClipBlock();
virtual Type type() override;
virtual QString Name() override;
virtual QString id() override;
virtual QString Description() override;
+6 -1
View File
@@ -24,6 +24,11 @@ GapBlock::GapBlock()
{
}
Block::Type GapBlock::type()
{
return kGap;
}
rational GapBlock::length()
{
return length_;
@@ -33,5 +38,5 @@ void GapBlock::set_length(const rational &length)
{
length_ = length;
RefreshSurrounds();
RefreshFollowing();
}
+2
View File
@@ -32,6 +32,8 @@ class GapBlock : public Block
public:
GapBlock();
virtual Type type() override;
virtual rational length() override;
virtual void set_length(const rational &length) override;
+2
View File
@@ -33,6 +33,8 @@ void NodeGraph::AddNode(Node *node)
connect(node, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SIGNAL(EdgeAdded(NodeEdgePtr)));
connect(node, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SIGNAL(EdgeRemoved(NodeEdgePtr)));
emit NodeAdded(node);
}
QList<Node *> NodeGraph::nodes()
+10
View File
@@ -51,6 +51,16 @@ public:
QList<Node*> nodes();
signals:
/**
* @brief Signal emitted when a Node is added to the graph
*/
void NodeAdded(Node* node);
/**
* @brief Signal emitted when a Node is removed from the graph
*/
void NodeRemoved(Node* node);
/**
* @brief Signal emitted when a member node of this graph has been connected to another (creating an "edge")
*/
+81
View File
@@ -20,7 +20,10 @@
#include "timeline.h"
#include <QDebug>
TimelineOutput::TimelineOutput() :
first_block_(nullptr),
current_block_(nullptr),
attached_timeline_(nullptr)
{
@@ -59,6 +62,8 @@ QString TimelineOutput::Description()
void TimelineOutput::AttachTimeline(TimelinePanel *timeline)
{
if (attached_timeline_ != nullptr) {
disconnect(attached_timeline_, SIGNAL(RequestInsertBlock(Block*, int)), this, SLOT(InsertBlock(Block*, int)));
attached_timeline_->Clear();
}
@@ -83,6 +88,8 @@ void TimelineOutput::AttachTimeline(TimelinePanel *timeline)
previous_block = previous_block->previous();
}
connect(attached_timeline_, SIGNAL(RequestInsertBlock(Block*, int)), this, SLOT(InsertBlock(Block*, int)));
}
}
@@ -96,6 +103,26 @@ NodeOutput *TimelineOutput::texture_output()
return texture_output_;
}
void TimelineOutput::Refresh()
{
Block* previous = attached_block();
first_block_ = nullptr;
// Find first block
while (previous != nullptr) {
// Cache block in "first", if this is indeed the first, its previous will be nullptr thus breaking the loop
first_block_ = previous;
previous = previous->previous();
}
if (first_block_ != nullptr) {
first_block_->RefreshFollowing();
}
}
void TimelineOutput::Process(const rational &time)
{
// This node is intended to connect to the end of the timeline, so being beyond its out point is considered the end
@@ -129,3 +156,57 @@ Block *TimelineOutput::attached_block()
{
return ValueToPtr<Block>(block_input_->get_value(0));
}
void TimelineOutput::InsertBlock(Block *block, int index)
{
// Add node and its connected nodes to graph
NodeGraph* graph = static_cast<NodeGraph*>(parent());
graph->AddNode(block);
// FIXME: We'll probably want to add more nodes than this?
Block* block_before = nullptr;
Block* block_after = first_block_;
for (int i=0;i<index;i++) {
block_before = block_after;
block_after = block_after->next();
if (block_after == nullptr) {
break;
}
}
if (block_before != nullptr && block_after != nullptr) {
// If neither blocks are null, we're inserting this block between them
// Disconnect existing blocks
Block::DisconnectBlocks(block_before, block_after);
Block::ConnectBlocks(block_before, block);
Block::ConnectBlocks(block, block_after);
} else if (block_before != nullptr) {
// We're at the end of the Sequence
// Disconnect previous ending clip from this one
NodeParam::DisconnectEdge(block_before->block_output(), block_input_);
NodeParam::ConnectEdge(block->block_output(), block_input_);
// Connect both blocks together
Block::ConnectBlocks(block_before, block);
} else if (block_after != nullptr) {
// We're at the start of the Sequence
// Connect both blocks together
Block::ConnectBlocks(block, block_after);
} else {
// Sequence is empty
NodeParam::ConnectEdge(block->block_output(), block_input_);
}
if (attached_timeline_ != nullptr) {
attached_timeline_->AddClip(static_cast<ClipBlock*>(block));
}
Refresh();
}
+7
View File
@@ -44,16 +44,23 @@ public:
NodeOutput* texture_output();
public slots:
void Refresh();
virtual void Process(const rational &time) override;
private:
Block* attached_block();
Block* first_block_;
Block* current_block_;
NodeInput* block_input_;
NodeOutput* texture_output_;
TimelinePanel* attached_timeline_;
private slots:
void InsertBlock(Block* block, int index);
};
#endif // TIMELINEOUTPUT_H
+1
View File
@@ -37,6 +37,7 @@ TimelinePanel::TimelinePanel(QWidget *parent) :
layout->addWidget(ruler_);
view_ = new TimelineView(this);
connect(view_, SIGNAL(RequestInsertBlock(Block*, int)), this, SIGNAL(RequestInsertBlock(Block*, int)));
layout->addWidget(view_);
connect(view_->horizontalScrollBar(), SIGNAL(valueChanged(int)), ruler_, SLOT(SetScroll(int)));
+3
View File
@@ -40,6 +40,9 @@ public:
protected:
virtual void changeEvent(QEvent* e) override;
signals:
void RequestInsertBlock(Block*, int);
private:
void Retranslate();
+36 -22
View File
@@ -47,33 +47,15 @@ void NodeView::SetGraph(NodeGraph *graph)
// If the graph is valid, add UI objects for each of its Nodes
if (graph_ != nullptr) {
connect(graph_, SIGNAL(NodeAdded(Node*)), this, SLOT(AddNode(Node*)));
connect(graph_, SIGNAL(NodeRemoved(Node*)), this, SLOT(RemoveNode(Node*)));
connect(graph_, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(AddEdge(NodeEdgePtr)));
connect(graph_, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(RemoveEdge(NodeEdgePtr)));
QList<Node*> graph_nodes = graph_->nodes();
foreach (Node* node, graph_nodes) {
NodeViewItem* item = new NodeViewItem();
item->SetNode(node);
scene_.addItem(item);
// Add a NodeViewEdge for each connection
QList<NodeParam*> node_params = node->parameters();
foreach (NodeParam* param, node_params) {
// We only bother working with outputs since eventually this will cover all inputs too
// (covering both would lead to duplicates since every edge connects to one input and one output)
if (param->type() == NodeParam::kOutput) {
const QVector<NodeEdgePtr>& edges = param->edges();
foreach(NodeEdgePtr edge, edges) {
AddEdge(edge);
}
}
}
AddNode(node);
}
}
}
@@ -122,6 +104,38 @@ NodeViewEdge *NodeView::EdgeToUIObject(NodeEdgePtr n)
return EdgeToUIObject(&scene_, n);
}
void NodeView::AddNode(Node* node)
{
NodeViewItem* item = new NodeViewItem();
item->SetNode(node);
scene_.addItem(item);
// Add a NodeViewEdge for each connection
QList<NodeParam*> node_params = node->parameters();
foreach (NodeParam* param, node_params) {
// We only bother working with outputs since eventually this will cover all inputs too
// (covering both would lead to duplicates since every edge connects to one input and one output)
if (param->type() == NodeParam::kOutput) {
const QVector<NodeEdgePtr>& edges = param->edges();
foreach(NodeEdgePtr edge, edges) {
AddEdge(edge);
}
}
}
}
void NodeView::RemoveNode(Node *node)
{
NodeViewItem* item = NodeToUIObject(scene(), node);
delete item;
}
void NodeView::AddEdge(NodeEdgePtr edge)
{
NodeViewEdge* edge_ui = new NodeViewEdge();
@@ -135,7 +149,7 @@ void NodeView::RemoveEdge(NodeEdgePtr edge)
{
NodeViewEdge* edge_ui = EdgeToUIObject(scene(), edge);
scene_.removeItem(edge_ui);
delete edge_ui;
}
void NodeView::ItemsChanged()
+16
View File
@@ -86,6 +86,22 @@ private:
QGraphicsScene scene_;
private slots:
/**
* @brief Slot when a Node is added to a graph (SetGraph() connects this)
*
* This should NEVER be called directly, only connected to a NodeGraph. To add a Node to the NodeGraph
* use NodeGraph::AddNode().
*/
void AddNode(Node* node);
/**
* @brief Slot when a Node is removed from a graph (SetGraph() connects this)
*
* This should NEVER be called directly, only connected to a NodeGraph. To remove a Node from the NodeGraph
* use NodeGraph::RemoveNode().
*/
void RemoveNode(Node* node);
/**
* @brief Slot when an edge is added to a graph (SetGraph() connects this)
*
@@ -23,6 +23,11 @@
#include <QWidget>
/**
* @brief A QWidget proxy for TimeRuler and TimelinePlayheadItem to allow them to share CSS values
*
* To allow Qt CSS customization (which is only available to QWidgets) to be accessed by TimeRuler
*/
class TimelinePlayhead : public QWidget
{
Q_OBJECT
+14 -5
View File
@@ -33,8 +33,7 @@ TimelineView::TimelineView(QWidget *parent) :
QGraphicsView(parent),
pointer_tool_(this),
import_tool_(this),
playhead_(0),
dragging_(false)
playhead_(0)
{
setScene(&scene_);
setAlignment(Qt::AlignLeft | Qt::AlignTop);
@@ -60,18 +59,22 @@ void TimelineView::AddClip(ClipBlock *clip)
clip_item->SetScale(scale_);
// Add to list of clip items that can be iterated through
clip_items_.append(clip_item);
clip_items_.insert(clip, clip_item);
// Add item to graphics scene
scene_.addItem(clip_item);
connect(clip, SIGNAL(Refreshed()), this, SLOT(BlockChanged()));
}
void TimelineView::SetScale(const double &scale)
{
scale_ = scale;
foreach (TimelineViewClipItem* item, clip_items_) {
item->SetScale(scale_);
QMapIterator<Block*, TimelineViewRect*> iterator(clip_items_);
while (iterator.hasNext()) {
iterator.value()->SetScale(scale_);
}
playhead_line_->SetScale(scale_);
@@ -169,7 +172,13 @@ void TimelineView::ClearGhosts()
}
}
void TimelineView::BlockChanged()
{
clip_items_[static_cast<Block*>(sender())]->UpdateRect();
}
TimelineView::Tool::Tool(TimelineView *parent) :
dragging_(false),
parent_(parent)
{
}
+24 -4
View File
@@ -32,6 +32,11 @@
#include "timelineviewghostitem.h"
#include "timelineviewplayheaditem.h"
/**
* @brief A widget for viewing and interacting Sequences
*
* This widget primarily exposes users to viewing and modifying Block nodes, usually through a TimelineOutput node.
*/
class TimelineView : public QGraphicsView
{
Q_OBJECT
@@ -40,6 +45,8 @@ public:
void AddClip(ClipBlock* clip);
void RemoveClip(ClipBlock* clip);
void SetScale(const double& scale);
void SetTimebase(const rational& timebase);
@@ -49,6 +56,9 @@ public:
public slots:
void SetTime(const int64_t time);
signals:
void RequestInsertBlock(Block* block, int index);
protected:
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
@@ -78,6 +88,11 @@ private:
TimelineView* parent();
protected:
bool dragging_;
QPoint drag_start_;
private:
TimelineView* parent_;
};
@@ -124,15 +139,20 @@ private:
int64_t playhead_;
QVector<TimelineViewClipItem*> clip_items_;
QMap<Block*, TimelineViewRect*> clip_items_;
QVector<TimelineViewGhostItem*> ghost_items_;
TimelineViewPlayheadItem* playhead_line_;
bool dragging_;
QPoint drag_start_;
private slots:
/**
* @brief Slot for when a Block node changes its parameters and the graphics need to update
*
* This slot does a static_cast on sender() to Block*, meaning all objects triggering this slot must be Blocks or
* derivatives.
*/
void BlockChanged();
};
#endif // TIMELINEVIEW_H
@@ -25,6 +25,9 @@
#include "node/block/clip/clip.h"
#include "timelineviewghostitem.h"
/**
* @brief A graphical representation of a ClipBlock
*/
class TimelineViewClipItem : public TimelineViewRect
{
public:
@@ -33,9 +36,9 @@ public:
ClipBlock* clip();
void SetClip(ClipBlock* clip);
protected:
virtual void UpdateRect() override;
protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
private:
@@ -24,6 +24,9 @@
#include "project/item/footage/footage.h"
#include "timelineviewrect.h"
/**
* @brief A graphical representation of changes the user is making before they apply it
*/
class TimelineViewGhostItem : public TimelineViewRect
{
public:
@@ -44,9 +47,10 @@ public:
StreamPtr stream();
void SetStream(StreamPtr f);
protected:
virtual void UpdateRect() override;
protected:
private:
rational in_;
rational out_;
@@ -24,6 +24,9 @@
#include "timelineplayhead.h"
#include "timelineviewrect.h"
/**
* @brief A graphical representation of the playhead on the Timeline
*/
class TimelineViewPlayheadItem : public TimelineViewRect
{
public:
@@ -33,9 +36,9 @@ public:
void SetTimebase(const rational& timebase);
protected:
virtual void UpdateRect() override;
protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
private:
+4 -1
View File
@@ -25,6 +25,9 @@
#include "common/rational.h"
/**
* @brief A base class for graphical representations of Block nodes
*/
class TimelineViewRect : public QGraphicsRectItem
{
public:
@@ -32,9 +35,9 @@ public:
void SetScale(const double& scale);
protected:
virtual void UpdateRect() = 0;
protected:
double TimeToScreenCoord(const rational& time);
double scale_;
+8
View File
@@ -101,6 +101,14 @@ void TimelineView::ImportTool::DragLeave(QDragLeaveEvent *event)
void TimelineView::ImportTool::DragDrop(QDropEvent *event)
{
if (parent()->HasGhosts()) {
foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) {
ClipBlock* clip = new ClipBlock();
clip->set_length(ghost->Out() - ghost->In());
emit parent()->RequestInsertBlock(clip, 0);
}
parent()->ClearGhosts();
event->accept();
+5 -5
View File
@@ -50,7 +50,7 @@ void TimelineView::PointerTool::MouseMove(QMouseEvent *event)
parent()->QGraphicsView::mouseMoveEvent(event);
if (!parent()->dragging_) {
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();
@@ -72,12 +72,12 @@ void TimelineView::PointerTool::MouseMove(QMouseEvent *event)
parent()->scene_.addItem(ghost);
}
parent()->drag_start_ = event->pos();
drag_start_ = event->pos();
}
parent()->dragging_ = true;
dragging_ = true;
} else if (!parent()->ghost_items_.isEmpty()) {
QPoint movement = event->pos() - parent()->drag_start_;
QPoint movement = event->pos() - drag_start_;
rational time_movement = parent()->ScreenToTime(movement.x());
@@ -105,5 +105,5 @@ void TimelineView::PointerTool::MouseRelease(QMouseEvent *event)
parent()->ClearGhosts();
parent()->dragging_ = false;
dragging_ = false;
}