timeline hierarchical length signalling

This commit is contained in:
itsmattkc
2019-10-07 02:33:52 +11:00
parent f34eb0dfb6
commit 31f11cd87d
17 changed files with 215 additions and 188 deletions
+30 -6
View File
@@ -42,6 +42,7 @@ TimelineOutput::TimelineOutput()
TrackList* list = new TrackList(this, track_input);
track_lists_[i] = list;
connect(list, SIGNAL(TrackListChanged()), this, SLOT(UpdateTrackCache()));
connect(list, SIGNAL(LengthChanged(const rational &)), this, SLOT(UpdateLength(const rational &)));
}
length_output_ = new NodeOutput("length_out");
@@ -79,12 +80,17 @@ NodeOutput *TimelineOutput::length_output()
return length_output_;
}
const rational &TimelineOutput::timeline_length()
{
return length_;
}
QVariant TimelineOutput::Value(NodeOutput *output, const rational &time)
{
if (output == length_output_) {
Q_UNUSED(time)
return QVariant::fromValue(timeline_length());
return QVariant::fromValue(length_);
}
return 0;
@@ -99,15 +105,33 @@ void TimelineOutput::UpdateTrackCache()
}
}
rational TimelineOutput::timeline_length()
void TimelineOutput::UpdateLength(const rational &length)
{
rational length = 0;
qDebug() << "Updating length! Received value:" << length;
foreach (TrackList* list, track_lists_) {
length = qMax(list->TrackListLength(), length);
// If this length is equal, no-op
if (length == length_) {
return;
}
return length;
// If this length is greater, this must be the new total length
if (length > length_) {
length_ = length;
emit LengthChanged(length_);
return;
}
// Otherwise, the new length is shorter and we'll have to manually determine what the new max length is
rational new_length = 0;
foreach (TrackList* list, track_lists_) {
new_length = qMax(new_length, list->TrackLength());
}
if (new_length != length_) {
length_ = new_length;
emit LengthChanged(length_);
}
}
void TimelineOutput::SetTimebase(const rational &timebase)
+6 -1
View File
@@ -56,11 +56,12 @@ public:
NodeOutput* length_output();
rational timeline_length();
const rational& timeline_length();
void SetTimebase(const rational &timebase);
signals:
void LengthChanged(const rational& length);
protected:
virtual QVariant Value(NodeOutput* output, const rational& time) override;
@@ -74,9 +75,13 @@ private:
NodeOutput* length_output_;
rational length_;
private slots:
void UpdateTrackCache();
void UpdateLength(const rational &length);
};
#endif // TIMELINEOUTPUT_H
+22 -16
View File
@@ -46,6 +46,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()));
current_track->SetIndex(track_cache_.size());
@@ -58,6 +59,8 @@ void TrackList::AttachTrack(TrackOutput *track)
current_track = current_track->next_track();
}
UpdateTotalLength();
}
void TrackList::DetachTrack(TrackOutput *track)
@@ -74,12 +77,15 @@ void TrackList::DetachTrack(TrackOutput *track)
disconnect(current_track, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(TrackEdgeRemoved(NodeEdgePtr)));
disconnect(current_track, SIGNAL(BlockAdded(Block*)), this, SLOT(TrackAddedBlock(Block*)));
disconnect(current_track, SIGNAL(BlockRemoved(Block*)), this, SLOT(TrackRemovedBlock(Block*)));
disconnect(current_track, SIGNAL(Refreshed()), this, SLOT(UpdateTotalLength()));
track_cache_.removeAll(current_track);
emit TrackListChanged();
current_track = current_track->next_track();
}
UpdateTotalLength();
}
void TrackList::TrackAddedBlock(Block *block)
@@ -102,22 +108,6 @@ TrackOutput *TrackList::TrackAt(int index)
return track_cache_.at(index);
}
rational TrackList::TrackListLength()
{
rational length = 0;
foreach (TrackOutput* track, track_cache_) {
length = qMax(length, track->in());
}
return length;
}
rational TrackList::TimelineLength()
{
return static_cast<TimelineOutput*>(parent())->timeline_length();
}
const rational &TrackList::Timebase()
{
return timebase_;
@@ -130,6 +120,11 @@ void TrackList::SetTimebase(const rational &timebase)
emit TimebaseChanged(timebase_);
}
const rational &TrackList::TrackLength()
{
return total_length_;
}
void TrackList::AddTrack()
{
TrackOutput* track = new TrackOutput();
@@ -223,3 +218,14 @@ NodeGraph *TrackList::GetParentGraph()
{
return static_cast<NodeGraph*>(parent()->parent());
}
void TrackList::UpdateTotalLength()
{
total_length_ = 0;
foreach (TrackOutput* track, track_cache_) {
total_length_ = qMax(total_length_, track->in());
}
emit LengthChanged(total_length_);
}
+9 -4
View File
@@ -47,14 +47,12 @@ public:
void RemoveTrack();
rational TrackListLength();
rational TimelineLength();
const rational& Timebase();
void SetTimebase(const rational& timebase);
const rational& TrackLength();
public slots:/**
* @brief Slot for when the track connection is added
*/
@@ -100,6 +98,8 @@ signals:
void TimebaseChanged(const rational &timebase);
void LengthChanged(const rational &length);
private:
NodeGraph* GetParentGraph();
@@ -111,6 +111,11 @@ private:
rational timebase_;
NodeInput* track_input_;
rational total_length_;
private slots:
void UpdateTotalLength();
};
#endif // TRACKLIST_H
+2 -2
View File
@@ -21,6 +21,8 @@ set(OLIVE_SOURCES
${OLIVE_SOURCES}
widget/timelineview/timelineplayhead.h
widget/timelineview/timelineplayhead.cpp
widget/timelineview/timelinescaledobject.h
widget/timelineview/timelinescaledobject.cpp
widget/timelineview/timelineview.h
widget/timelineview/timelineview.cpp
widget/timelineview/timelineviewrect.h
@@ -31,7 +33,5 @@ set(OLIVE_SOURCES
widget/timelineview/timelineviewenditem.cpp
widget/timelineview/timelineviewghostitem.h
widget/timelineview/timelineviewghostitem.cpp
widget/timelineview/timelineviewplayheaditem.h
widget/timelineview/timelineviewplayheaditem.cpp
PARENT_SCOPE
)
@@ -0,0 +1,12 @@
#include "timelinescaledobject.h"
TimelineScaledObject::TimelineScaledObject() :
scale_(1.0)
{
}
double TimelineScaledObject::TimeToScreenCoord(const rational &time)
{
return time.toDouble() * scale_;
}
@@ -0,0 +1,20 @@
#ifndef TIMELINESCALEDOBJECT_H
#define TIMELINESCALEDOBJECT_H
#include "common/rational.h"
class TimelineScaledObject
{
public:
TimelineScaledObject();
protected:
double TimeToScreenCoord(const rational& time);
double scale_;
private:
};
#endif // TIMELINESCALEDOBJECT_H
+50 -16
View File
@@ -45,7 +45,8 @@ TimelineView::TimelineView(Qt::Alignment vertical_alignment, QWidget *parent) :
hand_tool_(this),
zoom_tool_(this),
timeline_node_(nullptr),
playhead_(0)
playhead_(0),
use_tracklist_length_directly_(true)
{
Q_ASSERT(vertical_alignment == Qt::AlignTop || vertical_alignment == Qt::AlignBottom);
setAlignment(Qt::AlignLeft | vertical_alignment);
@@ -57,10 +58,6 @@ TimelineView::TimelineView(Qt::Alignment vertical_alignment, QWidget *parent) :
connect(&scene_, SIGNAL(changed(const QList<QRectF>&)), this, SLOT(UpdateSceneRect()));
// Create playhead line
playhead_line_ = new TimelineViewPlayheadItem();
scene_.addItem(playhead_line_);
// Create end item
end_item_ = new TimelineViewEndItem();
scene_.addItem(end_item_);
@@ -138,7 +135,9 @@ void TimelineView::SetScale(const double &scale)
ghost->SetScale(scale_);
}
playhead_line_->SetScale(scale_);
// Force redraw for playhead
viewport()->update();
end_item_->SetScale(scale_);
}
@@ -146,8 +145,6 @@ void TimelineView::SetTimebase(const rational &timebase)
{
timebase_ = timebase;
timebase_dbl_ = timebase_.toDouble();
playhead_line_->SetTimebase(timebase_);
}
void TimelineView::Clear()
@@ -175,6 +172,7 @@ void TimelineView::ConnectTimelineNode(TrackList *node)
disconnect(timeline_node_, SIGNAL(BlockRemoved(Block*)), this, SLOT(RemoveBlock(Block*)));
disconnect(timeline_node_, SIGNAL(TrackAdded(TrackOutput*)), this, SLOT(AddTrack(TrackOutput*)));
disconnect(timeline_node_, SIGNAL(TrackRemoved(TrackOutput*)), this, SLOT(RemoveTrack(TrackOutput*)));
disconnect(timeline_node_, SIGNAL(LengthChanged(const rational&)), this, SLOT(UpdateEndTimeFromTrackList(const rational&)));
Clear();
}
@@ -192,6 +190,7 @@ void TimelineView::ConnectTimelineNode(TrackList *node)
connect(timeline_node_, SIGNAL(BlockRemoved(Block*)), this, SLOT(RemoveBlock(Block*)));
connect(timeline_node_, SIGNAL(TrackAdded(TrackOutput*)), this, SLOT(AddTrack(TrackOutput*)));
connect(timeline_node_, SIGNAL(TrackRemoved(TrackOutput*)), this, SLOT(RemoveTrack(TrackOutput*)));
connect(timeline_node_, SIGNAL(LengthChanged(const rational&)), this, SLOT(UpdateEndTimeFromTrackList(const rational&)));
foreach (TrackOutput* track, timeline_node_->Tracks()) {
// Defer to the track to make all the block UI items necessary
@@ -223,11 +222,17 @@ void TimelineView::DeselectAll()
}
}
void TimelineView::SetUseTrackListLengthDirectly(bool use)
{
use_tracklist_length_directly_ = use;
}
void TimelineView::SetTime(const int64_t time)
{
playhead_ = time;
playhead_line_->SetPlayhead(playhead_);
// Force redraw for playhead
viewport()->update();
}
void TimelineView::mousePressEvent(QMouseEvent *event)
@@ -288,6 +293,24 @@ void TimelineView::resizeEvent(QResizeEvent *event)
UpdateSceneRect();
}
void TimelineView::drawForeground(QPainter *painter, const QRectF &rect)
{
QGraphicsView::drawForeground(painter, rect);
double x = TimeToScreenCoord(rational(playhead_ * timebase_.numerator(), timebase_.denominator()));
double width = TimeToScreenCoord(timebase_);
QRectF playhead_rect(x, rect.top(), width, rect.height());
painter->setPen(Qt::NoPen);
painter->setBrush(playhead_style_.PlayheadHighlightColor());
painter->drawRect(playhead_rect);
painter->setPen(playhead_style_.PlayheadColor());
painter->setBrush(Qt::NoBrush);
painter->drawLine(QLineF(playhead_rect.topLeft(), playhead_rect.bottomLeft()));
}
TimelineView::Tool *TimelineView::GetActiveTool()
{
switch (olive::core.tool()) {
@@ -400,6 +423,11 @@ void TimelineView::UserSetTime(const int64_t &time)
emit TimeChanged(time);
}
rational TimelineView::GetPlayheadTime()
{
return rational(playhead_ * timebase_.numerator(), timebase_.denominator());
}
void TimelineView::BlockChanged()
{
TimelineViewRect* rect = block_items_[static_cast<Block*>(sender())];
@@ -434,13 +462,7 @@ void TimelineView::UpdateSceneRect()
}
// Ensure the scene is always the full length of the timeline with a gap at the end to work with
if (timeline_node_ != nullptr) {
end_item_->SetEndTime(timeline_node_->TimelineLength());
end_item_->SetEndPadding(width()/4);
}
// Ensure playhead is the correct height
playhead_line_->SetHeight(viewport()->height()-1);
end_item_->SetEndPadding(width()/4);
// If the scene is already this rect, do nothing
if (scene_.sceneRect() == bounding_rect) {
@@ -449,3 +471,15 @@ void TimelineView::UpdateSceneRect()
scene_.setSceneRect(bounding_rect);
}
void TimelineView::UpdateEndTimeFromTrackList(const rational &length)
{
if (use_tracklist_length_directly_) {
SetEndTime(length);
}
}
void TimelineView::SetEndTime(const rational &length)
{
end_item_->SetEndTime(length);
}
+24 -6
View File
@@ -29,10 +29,10 @@
#include "node/block/clip/clip.h"
#include "node/output/timeline/timeline.h"
#include "timelineplayhead.h"
#include "timelineviewblockitem.h"
#include "timelineviewenditem.h"
#include "timelineviewghostitem.h"
#include "timelineviewplayheaditem.h"
#include "widget/timelineview/undo/undo.h"
#include "undo/undostack.h"
@@ -41,7 +41,7 @@
*
* This widget primarily exposes users to viewing and modifying Block nodes, usually through a TimelineOutput node.
*/
class TimelineView : public QGraphicsView
class TimelineView : public QGraphicsView, public TimelineScaledObject
{
Q_OBJECT
public:
@@ -57,6 +57,10 @@ public:
void DeselectAll();
void SetUseTrackListLengthDirectly(bool use);
void SetEndTime(const rational& length);
public slots:
void SetTimebase(const rational& timebase);
@@ -91,6 +95,8 @@ protected:
virtual void resizeEvent(QResizeEvent *event) override;
virtual void drawForeground(QPainter *painter, const QRectF &rect) override;
private:
class Tool
@@ -353,8 +359,6 @@ private:
QGraphicsScene scene_;
double scale_;
rational timebase_;
double timebase_dbl_;
@@ -367,12 +371,20 @@ private:
QVector<int> track_heights_;
TimelineViewPlayheadItem* playhead_line_;
TimelineViewEndItem* end_item_;
Tool* active_tool_;
TimelinePlayhead playhead_style_;
rational GetPlayheadTime();
void UpdatePlayheadRect();
QRect playhead_rect_;
bool use_tracklist_length_directly_;
private slots:
/**
* @brief Slot for when a Block node changes its parameters and the graphics need to update
@@ -386,6 +398,12 @@ private slots:
* @brief Slot called whenever the view resizes or the scene contents change to enforce minimum scene sizes
*/
void UpdateSceneRect();
/**
* @brief When the attached track list's end frame changes, update the graphical representation here
*/
void UpdateEndTimeFromTrackList(const rational& length);
};
#endif // TIMELINEVIEW_H
@@ -1,72 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "timelineviewplayheaditem.h"
#include <QDebug>
#include <QGraphicsScene>
#include <QPainter>
#include <QtMath>
TimelineViewPlayheadItem::TimelineViewPlayheadItem(QGraphicsItem *parent) :
TimelineViewRect(parent),
playhead_(0)
{
// Ensure this item is always on top
setZValue(100);
}
void TimelineViewPlayheadItem::SetPlayhead(const int64_t &playhead)
{
playhead_ = playhead;
UpdateRect();
}
void TimelineViewPlayheadItem::SetTimebase(const rational &timebase)
{
timebase_ = timebase;
UpdateRect();
}
void TimelineViewPlayheadItem::UpdateRect()
{
double x = TimeToScreenCoord(rational(playhead_ * timebase_.numerator(), timebase_.denominator()));
double width = TimeToScreenCoord(timebase_);
setRect(0, 0, width, height_);
setPos(x, y_);
}
void TimelineViewPlayheadItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
// FIXME: Make adjustable through CSS
painter->setPen(Qt::NoPen);
painter->setBrush(style_.PlayheadHighlightColor());
painter->drawRect(rect());
painter->setPen(style_.PlayheadColor());
painter->setBrush(Qt::NoBrush);
painter->drawLine(QLineF(rect().topLeft(), rect().bottomLeft()));
}
@@ -1,52 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef TIMELINEVIEWPLAYHEADITEM_H
#define TIMELINEVIEWPLAYHEADITEM_H
#include "timelineplayhead.h"
#include "timelineviewrect.h"
/**
* @brief A graphical representation of the playhead on the Timeline
*/
class TimelineViewPlayheadItem : public TimelineViewRect
{
public:
TimelineViewPlayheadItem(QGraphicsItem* parent = nullptr);
void SetPlayhead(const int64_t& playhead);
void SetTimebase(const rational& timebase);
virtual void UpdateRect() override;
protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
private:
int64_t playhead_;
rational timebase_;
TimelinePlayhead style_;
};
#endif // TIMELINEVIEWPLAYHEADITEM_H
@@ -22,7 +22,6 @@
TimelineViewRect::TimelineViewRect(QGraphicsItem* parent) :
QGraphicsRectItem(parent),
scale_(1.0),
y_(0),
height_(0)
{
@@ -69,8 +68,3 @@ void TimelineViewRect::SetScale(const double &scale)
UpdateRect();
}
double TimelineViewRect::TimeToScreenCoord(const rational &time)
{
return time.toDouble() * scale_;
}
+2 -6
View File
@@ -23,12 +23,12 @@
#include <QGraphicsRectItem>
#include "common/rational.h"
#include "timelinescaledobject.h"
/**
* @brief A base class for graphical representations of Block nodes
*/
class TimelineViewRect : public QGraphicsRectItem
class TimelineViewRect : public QGraphicsRectItem, public TimelineScaledObject
{
public:
TimelineViewRect(QGraphicsItem* parent = nullptr);
@@ -47,10 +47,6 @@ public:
virtual void UpdateRect() = 0;
protected:
double TimeToScreenCoord(const rational& time);
double scale_;
int y_;
int height_;
+3 -1
View File
@@ -141,11 +141,13 @@ bool TimelineView::Tool::SnapPoint(QList<rational> start_times, rational* moveme
}
if (snap_points & kSnapToPlayhead) {
qreal playhead_pos = parent()->playhead_line_->x();
rational playhead_abs_time = rational(parent()->playhead_ * parent()->timebase_.numerator(),
parent()->timebase_.denominator());
qreal playhead_pos = playhead_abs_time.toDouble() * parent()->scale_;
AttemptSnap(proposed_pts, playhead_pos, start_times, playhead_abs_time, movement, &diff);
}
+11
View File
@@ -39,6 +39,11 @@ void TimelineView::ZoomTool::MouseRelease(QMouseEvent *event)
{
double scale = parent()->scale_;
QPointF center_location = parent()->mapToScene(event->pos());
// Normalize zoom location for 1.0 scale
double scaled_x = center_location.x() / scale;
if (event->modifiers() & Qt::AltModifier) {
// Zoom out if the user clicks while holding Alt
scale *= 0.5;
@@ -49,4 +54,10 @@ void TimelineView::ZoomTool::MouseRelease(QMouseEvent *event)
parent()->SetScale(scale);
emit parent()->ScaleChanged(scale);
// Adjust zoom location for new scale
scaled_x *= scale;
center_location.setX(scaled_x);
parent()->centerOn(center_location);
}
@@ -61,6 +61,9 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
}
}
// Split viewer 50/50
view_splitter->setSizes({INT_MAX, INT_MAX});
// FIXME: Magic number
SetScale(90.0);
}
@@ -105,6 +108,10 @@ void TimelineWidget::SetTime(const int64_t &timestamp)
void TimelineWidget::ConnectTimelineNode(TimelineOutput *node)
{
if (timeline_node_ != nullptr) {
disconnect(timeline_node_, SIGNAL(LengthChanged(const rational&)), this, SLOT(UpdateTimelineLength(const rational&)));
}
timeline_node_ = node;
int track_type = 0;
@@ -114,6 +121,14 @@ void TimelineWidget::ConnectTimelineNode(TimelineOutput *node)
track_type++;
}
if (timeline_node_ != nullptr) {
connect(timeline_node_, SIGNAL(LengthChanged(const rational&)), this, SLOT(UpdateTimelineLength(const rational&)));
foreach (TimelineView* view, views_) {
view->SetEndTime(timeline_node_->timeline_length());
}
}
}
void TimelineWidget::DisconnectTimelineNode()
@@ -313,3 +328,10 @@ void TimelineWidget::UpdateInternalTime(const int64_t &timestamp)
{
playhead_ = timestamp;
}
void TimelineWidget::UpdateTimelineLength(const rational &length)
{
foreach (TimelineView* view, views_) {
view->SetEndTime(length);
}
}
@@ -79,6 +79,8 @@ private slots:
void UpdateInternalTime(const int64_t& timestamp);
void UpdateTimelineLength(const rational& length);
};
#endif // TIMELINEWIDGET_H