implemented new block system

Fixes recurring design issue that the Blocks were a frequent exception to the
DAG concept. The Blocks connecting to each others inputs/outputs while not
necessarily being "dependent" on each other to produce an image continually
causes issues while trying to create a rendering code path. This redesign
should provide a more "directed" approach to the directed acyclic graph.
This commit is contained in:
itsmattkc
2019-12-02 00:37:03 +11:00
parent 6314c80cbc
commit 79ee3bc3df
13 changed files with 279 additions and 240 deletions
+25 -23
View File
@@ -31,9 +31,6 @@ Block::Block() :
buffer_output_ = new NodeOutput("buffer_out");
AddParameter(buffer_output_);
connect(this, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(EdgeAddedSlot(NodeEdgePtr)), Qt::DirectConnection);
connect(this, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(EdgeRemovedSlot(NodeEdgePtr)), Qt::DirectConnection);
}
QString Block::Category()
@@ -51,6 +48,16 @@ const rational &Block::out()
return out_point_;
}
void Block::set_in(const rational &in)
{
in_point_ = in;
}
void Block::set_out(const rational &out)
{
out_point_ = out;
}
const rational& Block::length()
{
return length_;
@@ -60,13 +67,17 @@ void Block::set_length(const rational &length)
{
Q_ASSERT(length > 0);
if (length == length_) {
return;
}
LockUserInput();
length_ = length;
UnlockUserInput();
Refresh();
emit LengthChanged(length_);
}
void Block::set_length_and_media_in(const rational &length)
@@ -88,6 +99,16 @@ Block *Block::next()
return next_;
}
void Block::set_previous(Block *previous)
{
previous_ = previous;
}
void Block::set_next(Block *next)
{
next_ = next;
}
QVariant Block::Value(NodeOutput *output)
{
if (output == block_output_) {
@@ -98,25 +119,6 @@ QVariant Block::Value(NodeOutput *output)
return 0;
}
void Block::Refresh()
{
// Set in point to the out point of the previous Node
if (previous() != nullptr) {
in_point_ = previous()->out();
} else {
in_point_ = 0;
}
// Update out point by adding this clip's length to the just calculated in point
out_point_ = in_point_ + length();
emit Refreshed();
if (next() != nullptr) {
next()->Refresh();
}
}
NodeOutput *Block::buffer_output()
{
return buffer_output_;
+12 -20
View File
@@ -42,7 +42,7 @@ public:
enum Type {
kClip,
kGap,
kEnd
kTrack
};
virtual Type type() = 0;
@@ -51,6 +51,8 @@ public:
const rational& in();
const rational& out();
void set_in(const rational& in);
void set_out(const rational& out);
const rational &length();
void set_length(const rational &length);
@@ -58,6 +60,8 @@ public:
Block* previous();
Block* next();
void set_previous(Block* previous);
void set_next(Block* next);
NodeOutput* buffer_output();
NodeOutput* block_output();
@@ -80,20 +84,6 @@ public:
virtual QVariant Value(NodeOutput* output) override;
public slots:
/**
* @brief Refreshes internal cache of in/out points up to date
*
* A block can only know truly know its in point by adding all the lengths of the clips before it. Since this can
* become timeconsuming, blocks cache their in and out points for easy access, however this does mean their caches
* need to stay up to date to provide accurate results. Whenever this or any surrounding Block is changed, it's
* recommended to call Refresh().
*
* This function specifically sets the in point to the out point of the previous clip and sets its out point to the
* in point + this block's length. Therefore, before calling Refresh() on a Block, it's necessary that all the
* Blocks before it are accurate and up to date. You may need to traverse through the Block list (using previous())
* and run Refresh() on all Blocks sequentially.
*/
virtual void Refresh();
signals:
/**
@@ -103,6 +93,8 @@ signals:
*/
void Refreshed();
void LengthChanged(const rational& length);
protected:
rational SequenceToMediaTime(const rational& sequence_time);
@@ -110,18 +102,18 @@ protected:
static void CopyParameters(Block* source, Block* dest);
Block* previous_;
Block* next_;
private:
NodeOutput* block_output_;
NodeOutput* buffer_output_;
rational in_point_;
rational out_point_;
rational length_;
rational media_in_;
Block* previous_;
Block* next_;
rational in_point_;
rational out_point_;
QString block_name_;
+2 -2
View File
@@ -47,7 +47,7 @@ void TrackList::AttachTrack(TrackOutput *track)
connect(current_track, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(TrackEdgeRemoved(NodeEdgePtr)));
connect(current_track, SIGNAL(BlockAdded(Block*)), this, SLOT(TrackAddedBlock(Block*)));
connect(current_track, SIGNAL(BlockRemoved(Block*)), this, SLOT(TrackRemovedBlock(Block*)));
connect(current_track, SIGNAL(Refreshed()), this, SLOT(UpdateTotalLength()));
connect(current_track, SIGNAL(TrackLengthChanged()), this, SLOT(UpdateTotalLength()));
current_track->SetIndex(track_cache_.size());
current_track->set_track_type(type_);
@@ -219,7 +219,7 @@ void TrackList::UpdateTotalLength()
total_length_ = 0;
foreach (TrackOutput* track, track_cache_) {
total_length_ = qMax(total_length_, track->in());
total_length_ = qMax(total_length_, track->track_length());
}
emit LengthChanged(total_length_);
+199 -145
View File
@@ -32,6 +32,9 @@ TrackOutput::TrackOutput() :
{
block_input_ = new NodeInputArray("block_in");
AddParameter(block_input_);
connect(block_input_, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(BlockConnected(NodeEdgePtr)));
connect(block_input_, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(BlockDisconnected(NodeEdgePtr)));
connect(block_input_, SIGNAL(SizeChanged(int)), this, SLOT(BlockListSizeChanged(int)));
track_input_ = new NodeInput("track_in");
track_input_->set_data_type(NodeParam::kTrack);
@@ -54,7 +57,7 @@ const TrackType& TrackOutput::track_type()
Block::Type TrackOutput::type()
{
return kEnd;
return kTrack;
}
Block *TrackOutput::copy()
@@ -83,33 +86,6 @@ QString TrackOutput::Description()
"a Sequence.");
}
void TrackOutput::Refresh()
{
QVector<Block*> detect_attached_blocks;
Block* prev = previous();
while (prev != nullptr) {
detect_attached_blocks.prepend(prev);
if (!block_cache_.contains(prev)) {
emit BlockAdded(prev);
}
prev = prev->previous();
}
foreach (Block* b, block_cache_) {
if (!detect_attached_blocks.contains(b)) {
// If the current block was removed, stop referencing it
emit BlockRemoved(b);
}
}
block_cache_ = detect_attached_blocks;
Block::Refresh();
}
const int &TrackOutput::Index()
{
return index_;
@@ -192,87 +168,42 @@ QVariant TrackOutput::Value(NodeOutput *output)
if (output == track_output_) {
// Set track output correctly
return PtrToValue(this);
/*} else if (output == buffer_output()) {
ValidateCurrentBlock(in);
if (current_block_ != this) {
// At this point, we must have found the correct block so we use its texture output to produce the image
return current_block_->buffer_output()->get_value(in, out);
}
// No texture is valid
return 0;*/
}
// Run default node processing
return Block::Value(output);
}
void TrackOutput::InsertBlockBetweenBlocks(Block *block, Block *before, Block *after)
{
AddBlockToGraph(block);
Block::DisconnectBlocks(before, after);
Block::ConnectBlocks(before, block);
Block::ConnectBlocks(block, after);
}
void TrackOutput::InsertBlockBefore(Block* block, Block* after)
{
Block* before = after->previous();
// If a block precedes this one, just insert between them
if (before != nullptr) {
InsertBlockBetweenBlocks(block, before, after);
} else {
AddBlockToGraph(block);
// Otherwise, just connect the block since there's no before clip to insert between
Block::ConnectBlocks(block, after);
}
InsertBlockAtIndex(block, block_cache_.indexOf(after));
}
void TrackOutput::InsertBlockAfter(Block *block, Block *before)
{
InsertBlockBetweenBlocks(block, before, before->next());
int before_index = block_cache_.indexOf(before);
Q_ASSERT(before_index >= 0);
if (before_index == block_cache_.size() - 1) {
AppendBlock(block);
} else {
InsertBlockAtIndex(block, before_index + 1);
}
}
void TrackOutput::PrependBlock(Block *block)
{
AddBlockToGraph(block);
if (block_cache_.isEmpty()) {
ConnectBlockInternal(block);
} else {
Block::ConnectBlocks(block, block_cache_.first());
}
InsertBlockAtIndex(block, 0);
}
void TrackOutput::InsertBlockAtIndex(Block *block, int index)
{
AddBlockToGraph(block);
if (block_cache_.isEmpty()) {
// If there are no blocks connected, the index doesn't matter. Just connect it.
ConnectBlockInternal(block);
} else if (index == 0) {
// If the index is 0, it goes at the very beginning
PrependBlock(block);
} else if (index >= block_cache_.size()) {
// Append Block at the end
AppendBlock(block);
} else {
// Insert Block just before the Block currently at that index so that it becomes the new Block at that index
InsertBlockBetweenBlocks(block, block_cache_.at(index - 1), block_cache_.at(index));
}
block_input_->InsertAt(index);
NodeParam::ConnectEdge(block->block_output(),
block_input_->ParamAt(index));
}
void TrackOutput::AppendBlock(Block *block)
@@ -281,23 +212,15 @@ void TrackOutput::AppendBlock(Block *block)
BlockInvalidateCache();
if (block_cache_.isEmpty()) {
ConnectBlockInternal(block);
} else {
InsertBlockBetweenBlocks(block, block_cache_.last(), this);
}
int last_index = block_input_->GetSize();
block_input_->Append();
NodeParam::ConnectEdge(block->block_output(),
block_input_->ParamAt(last_index));
UnblockInvalidateCache();
// Invalidate area that block was added to
InvalidateCache(block->in(), in());
}
void TrackOutput::ConnectBlockInternal(Block *block)
{
AddBlockToGraph(block);
Block::ConnectBlocks(block, this);
InvalidateCache(block->in(), track_length());
}
void TrackOutput::AddBlockToGraph(Block *block)
@@ -322,18 +245,7 @@ void TrackOutput::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);
}
ReplaceBlock(block, gap);
}
void TrackOutput::RippleRemoveBlock(Block *block)
@@ -342,24 +254,13 @@ void TrackOutput::RippleRemoveBlock(Block *block)
rational remove_in = block->in();
Block* previous = block->previous();
Block* next = block->next();
int index_of_block_to_remove = block_cache_.indexOf(block);
if (previous != nullptr) {
Block::DisconnectBlocks(previous, block);
}
if (next != nullptr) {
Block::DisconnectBlocks(block, next);
}
if (previous != nullptr && next != nullptr) {
Block::ConnectBlocks(previous, next);
}
block_input_->RemoveAt(index_of_block_to_remove);
UnblockInvalidateCache();
InvalidateCache(remove_in, in());
InvalidateCache(remove_in, track_length());
// FIXME: Should there be removing the Blocks from the graph?
}
@@ -368,23 +269,17 @@ void TrackOutput::ReplaceBlock(Block *old, Block *replace)
{
Q_ASSERT(old->length() == replace->length());
Block* previous = old->previous();
Block* next = old->next();
BlockInvalidateCache();
AddBlockToGraph(replace);
// Disconnect old block from its surroundings
if (previous != nullptr) {
Block::DisconnectBlocks(previous, old);
Block::ConnectBlocks(previous, replace);
}
int index_of_old_block = block_cache_.indexOf(old);
if (next != nullptr) {
Block::DisconnectBlocks(old, next);
Block::ConnectBlocks(replace, next);
}
NodeParam::DisconnectEdge(old->block_output(),
block_input_->ParamAt(index_of_old_block));
NodeParam::ConnectEdge(replace->block_output(),
block_input_->ParamAt(index_of_old_block));
UnblockInvalidateCache();
@@ -393,14 +288,173 @@ void TrackOutput::ReplaceBlock(Block *old, Block *replace)
TrackOutput *TrackOutput::TrackFromBlock(Block *block)
{
Block* n = block;
NodeOutput* output = block->block_output();
// Find last valid block in Sequence and assume its a track
while (n->next() != nullptr) {
n = n->next();
foreach (NodeEdgePtr edge, output->edges()) {
TrackOutput* track_test = dynamic_cast<TrackOutput*>(edge->input()->parentNode());
if (track_test) {
return track_test;
}
}
// Downside of this approach is the usage of dynamic_cast, alternative would be looping through all known tracks and
// seeing if the contain the Block, but this seems slower
return dynamic_cast<TrackOutput*>(n);
return nullptr;
}
const rational &TrackOutput::track_length()
{
return track_length_;
}
bool TrackOutput::IsTrack()
{
return true;
}
void TrackOutput::UpdateInOutFrom(int index)
{
Q_ASSERT(index >= 0);
Q_ASSERT(index < block_cache_.size());
rational new_track_length;
for (int i=index;i<block_cache_.size();i++) {
Block* b = block_cache_.at(i);
if (b) {
rational prev_out;
// Find previous block and retrieve its out point (if there isn't one, this in will be set to 0)
for (int j=i-1;j>=0;j--) {
Block* previous = block_cache_.at(j);
if (previous) {
prev_out = previous->out();
break;
}
}
rational new_out = prev_out + b->length();
b->set_in(prev_out);
b->set_out(new_out);
new_track_length = new_out;
emit b->Refreshed();
}
}
// Update track length
if (new_track_length != track_length_) {
track_length_ = new_track_length;
emit TrackLengthChanged();
}
}
void TrackOutput::UpdatePreviousAndNextOfIndex(int index)
{
Block* ref = block_cache_.at(index);
Block* previous = nullptr;
Block* next = nullptr;
// Find previous
for (int i=index-1;i>=0;i--) {
previous = block_cache_.at(i);
if (previous) {
break;
}
}
// Find next
for (int i=index+1;i<block_cache_.size();i++) {
next = block_cache_.at(i);
if (next) {
break;
}
}
if (ref) {
// Link blocks together
ref->set_previous(previous);
ref->set_next(next);
if (previous)
previous->set_next(ref);
if (next)
next->set_previous(ref);
} else {
// Link previous and next together
if (previous)
previous->set_next(next);
if (next)
next->set_previous(previous);
}
}
void TrackOutput::BlockConnected(NodeEdgePtr edge)
{
int block_index = block_input_->IndexOfSubParameter(edge->input());
Q_ASSERT(block_index >= 0);
Block* connected_block = dynamic_cast<Block*>(edge->output()->parentNode());
block_cache_.replace(block_index, connected_block);
UpdatePreviousAndNextOfIndex(block_index);
UpdateInOutFrom(block_index);
if (connected_block) {
connect(connected_block, SIGNAL(LengthChanged(const rational&)), this, SLOT(BlockLengthChanged()));
emit BlockAdded(connected_block);
}
}
void TrackOutput::BlockDisconnected(NodeEdgePtr edge)
{
int block_index = block_input_->IndexOfSubParameter(edge->input());
Q_ASSERT(block_index >= 0);
block_cache_.replace(block_index, nullptr);
UpdatePreviousAndNextOfIndex(block_index);
UpdateInOutFrom(block_index);
Block* connected_block = dynamic_cast<Block*>(edge->output()->parentNode());
if (connected_block) {
disconnect(connected_block, SIGNAL(LengthChanged(const rational&)), this, SLOT(BlockLengthChanged()));
// Update previous and next references
emit BlockRemoved(connected_block);
}
}
void TrackOutput::BlockListSizeChanged(int size)
{
int old_size = block_cache_.size();
block_cache_.resize(size);
if (size > old_size) {
// Fill new slots with nullptr
for (int i=old_size;i<size;i++) {
block_cache_.replace(i, nullptr);
}
}
}
void TrackOutput::BlockLengthChanged()
{
// Assumes sender is a Block
Block* b = static_cast<Block*>(sender());
int index = block_cache_.indexOf(b);
Q_ASSERT(index >= 0);
UpdateInOutFrom(index);
}
+22 -19
View File
@@ -45,8 +45,6 @@ public:
virtual QString Category() override;
virtual QString Description() override;
virtual void Refresh() override;
const int& Index();
void SetIndex(const int& index);
@@ -79,13 +77,6 @@ public:
*/
void InsertBlockAtIndex(Block* block, int index);
/**
* @brief Inserts a Block between two other Blocks
*
* Disconnects `before` and `after`, and connects them to `block` with `block` in between.
*/
void InsertBlockBetweenBlocks(Block* block, Block* before, Block* after);
/**
* @brief Inserts Block after another Block
*
@@ -131,6 +122,12 @@ public:
*/
void AddBlockToGraph(Block* block);
static TrackOutput* TrackFromBlock(Block* block);
const rational& track_length();
virtual bool IsTrack() override;
signals:
/**
* @brief Signal emitted when a Block is added to this Track
@@ -142,21 +139,18 @@ signals:
*/
void BlockRemoved(Block* block);
/**
* @brief Signal emitted when the length of the track has changed
*/
void TrackLengthChanged();
protected:
virtual QVariant Value(NodeOutput* output) override;
private:
/**
* @brief Sets this Block as the only block in the Timeline (creating essentially a one clip sequence)
*/
void ConnectBlockInternal(Block* block);
void UpdateInOutFrom(int index);
/**
* @brief Disconnects t
*/
void RemoveBlockInternal();
static TrackOutput* TrackFromBlock(Block* block);
void UpdatePreviousAndNextOfIndex(int index);
QVector<Block*> block_cache_;
@@ -168,11 +162,20 @@ private:
TrackType track_type_;
rational track_length_;
int block_invalidate_cache_stack_;
int index_;
private slots:
void BlockConnected(NodeEdgePtr edge);
void BlockDisconnected(NodeEdgePtr edge);
void BlockListSizeChanged(int size);
void BlockLengthChanged();
};
+2 -2
View File
@@ -276,7 +276,7 @@ void TimelineWidget::GoToNextCut()
int64_t closest_cut = INT64_MAX;
foreach (TrackOutput* track, timeline_node_->Tracks()) {
int64_t this_track_closest_cut = olive::time_to_timestamp(track->in(), timebase());
int64_t this_track_closest_cut = olive::time_to_timestamp(track->track_length(), timebase());
if (this_track_closest_cut <= playhead_) {
this_track_closest_cut = INT64_MAX;
@@ -592,7 +592,7 @@ void TimelineWidget::AddBlock(Block *block, TrackReference track)
connect(block, SIGNAL(Refreshed()), this, SLOT(BlockChanged()));
break;
}
case Block::kEnd:
case Block::kTrack:
// Do nothing
break;
}
+1 -1
View File
@@ -513,7 +513,7 @@ rational TimelineWidget::PointerTool::ValidateOutTrimming(rational movement,
if (prevent_overwriting) {
// Determine if there's a block in the way
Block* next = block->next();
while (next != nullptr && next->type() != Block::kEnd) {
while (next != nullptr) {
if (next->type() == Block::kClip) {
latest_out = qMin(latest_out, next->in());
break;
+1 -3
View File
@@ -129,9 +129,7 @@ void TimelineWidget::RippleTool::InitiateGhosts(TimelineViewBlockItem *clicked_i
Block* block_before_ripple = track->NearestBlockBefore(earliest_ripple);
// If block is null, there will be no blocks after to ripple
if (block_before_ripple != nullptr
&& block_before_ripple->type() != Block::kEnd
&& block_before_ripple->next()->type() != Block::kEnd) {
if (block_before_ripple != nullptr) {
TimelineViewGhostItem* ghost;
TrackReference track_ref(track->track_type(), track->Index());
+1 -1
View File
@@ -90,7 +90,7 @@ void TimelineWidget::RollingTool::InitiateGhosts(TimelineViewBlockItem *clicked_
if (ghost->mode() == olive::timeline::kTrimIn && ghost_block->previous() != nullptr) {
// Add an extra Ghost for the previous block
AddGhostFromBlock(ghost_block->previous(), ghost->Track(), olive::timeline::kTrimOut);
} else if (ghost->mode() == olive::timeline::kTrimOut && ghost_block->next() != nullptr && ghost_block->next()->type() != Block::kEnd) {
} else if (ghost->mode() == olive::timeline::kTrimOut && ghost_block->next() != nullptr) {
AddGhostFromBlock(ghost_block->next(), ghost->Track(), olive::timeline::kTrimIn);
}
}
+1 -1
View File
@@ -81,7 +81,7 @@ void TimelineWidget::SlideTool::InitiateGhosts(TimelineViewBlockItem *clicked_it
AddGhostFromBlock(ghost_block->previous(), ghost->Track(), olive::timeline::kTrimOut);
}
if (ghost_block->next() != nullptr && ghost_block->next()->type() != Block::kEnd) {
if (ghost_block->next() != nullptr) {
AddGhostFromBlock(ghost_block->next(), ghost->Track(), olive::timeline::kTrimIn);
}
}
+12 -21
View File
@@ -38,18 +38,6 @@ Node* TakeNodeFromParentGraph(Node* n, QObject* new_parent = nullptr)
return n;
}
TrackOutput* TrackFromBlock(Block* b)
{
Block* next = b;
do {
next = next->next();
} while (next != nullptr && next->type() != Block::kEnd);
// A little hacky, but this should either be a TrackOutput* or nullptr
return static_cast<TrackOutput*>(next);
}
BlockResizeCommand::BlockResizeCommand(Block *block, rational new_length, QUndoCommand* parent) :
QUndoCommand(parent),
block_(block),
@@ -108,8 +96,7 @@ TrackRippleRemoveBlockCommand::TrackRippleRemoveBlockCommand(TrackOutput *track,
QUndoCommand(parent),
track_(track),
block_(block),
before_(block->previous()),
after_(block->next())
before_(block->previous())
{
}
@@ -120,7 +107,7 @@ void TrackRippleRemoveBlockCommand::redo()
void TrackRippleRemoveBlockCommand::undo()
{
track_->InsertBlockBetweenBlocks(block_, before_, after_);
track_->InsertBlockAfter(block_, before_);
}
TrackInsertBlockBetweenBlocksCommand::TrackInsertBlockBetweenBlocksCommand(TrackOutput *track,
@@ -138,7 +125,7 @@ TrackInsertBlockBetweenBlocksCommand::TrackInsertBlockBetweenBlocksCommand(Track
void TrackInsertBlockBetweenBlocksCommand::redo()
{
track_->InsertBlockBetweenBlocks(block_, before_, after_);
track_->InsertBlockAfter(block_, before_);
}
void TrackInsertBlockBetweenBlocksCommand::undo()
@@ -245,7 +232,7 @@ void TrackRippleRemoveAreaCommand::redo()
track_->AppendBlock(insert_);
} else {
// This is somewhere in the middle of the Sequence
track_->InsertBlockBetweenBlocks(insert_, trim_out_, trim_in_);
track_->InsertBlockAfter(insert_, trim_out_);
}
}
@@ -320,14 +307,14 @@ void TrackPlaceBlockCommand::redo()
track_ = timeline_->TrackAt(track_index_);
append_ = (in_ >= track_->in());
append_ = (in_ >= track_->track_length());
// Check if the placement location is past the end of the timeline
if (append_) {
if (in_ > track_->in()) {
if (in_ > track_->track_length()) {
// If so, insert a gap here
gap_ = new GapBlock();
gap_->set_length(in_ - track_->in());
gap_->set_length(in_ - track_->track_length());
track_->AppendBlock(gap_);
}
@@ -471,7 +458,11 @@ BlockSplitPreservingLinksCommand::BlockSplitPreservingLinksCommand(const QVector
Block* b = blocks.at(j);
if (b->in() < time && b->out() > time) {
BlockSplitCommand* split_command = new BlockSplitCommand(TrackFromBlock(b), b, time, this);
TrackOutput* track = TrackOutput::TrackFromBlock(b);
Q_ASSERT(track);
BlockSplitCommand* split_command = new BlockSplitCommand(track, b, time, this);
splits.replace(j, split_command->new_block());
} else {
splits.replace(j, nullptr);
-1
View File
@@ -80,7 +80,6 @@ private:
Block* block_;
Block* before_;
Block* after_;
};
class TrackPrependBlockCommand : public QUndoCommand {
@@ -118,7 +118,7 @@ void TimelineViewBlockItem::paint(QPainter *painter, const QStyleOptionGraphicsI
painter->fillRect(rect(), Qt::white);
}
break;
case Block::kEnd:
case Block::kTrack:
break;
}
}