timeline: added ability to move clips by sample in addition to by frame

This commit is contained in:
itsmattkc
2020-04-01 13:55:19 +11:00
parent 061f7ac20d
commit 76f260cb3a
7 changed files with 127 additions and 21 deletions
+10
View File
@@ -263,3 +263,13 @@ int64_t Timecode::time_to_timestamp(const double &time, const rational &timebase
{
return qRound64(time * timebase.flipped().toDouble());
}
int64_t Timecode::rescale_timestamp(const int64_t &ts, const rational &source, const rational &dest)
{
return qRound64(static_cast<double>(ts) * source.toDouble() / dest.toDouble());
}
int64_t Timecode::rescale_timestamp_ceil(const int64_t &ts, const rational &source, const rational &dest)
{
return qCeil(static_cast<double>(ts) * source.toDouble() / dest.toDouble());
}
+3
View File
@@ -57,6 +57,9 @@ public:
static int64_t time_to_timestamp(const rational& time, const rational& timebase);
static int64_t time_to_timestamp(const double& time, const rational& timebase);
static int64_t rescale_timestamp(const int64_t& ts, const rational& source, const rational& dest);
static int64_t rescale_timestamp_ceil(const int64_t& ts, const rational& source, const rational& dest);
static rational timestamp_to_time(const int64_t& timestamp, const rational& timebase);
static QString time_to_timecode(const rational& time, const rational& timebase, const Display &display, bool show_plus_if_positive = false);
+92 -10
View File
@@ -6,6 +6,7 @@
#include "core.h"
#include "common/timecodefunctions.h"
#include "dialog/sequence/sequence.h"
#include "dialog/speedduration/speedduration.h"
#include "node/block/transition/transition.h"
#include "tool/tool.h"
@@ -16,7 +17,8 @@
TimelineWidget::TimelineWidget(QWidget *parent) :
TimeBasedWidget(true, true, parent),
rubberband_(QRubberBand::Rectangle, this),
active_tool_(nullptr)
active_tool_(nullptr),
use_audio_time_units_(false)
{
QVBoxLayout* vert_layout = new QVBoxLayout(this);
vert_layout->setSpacing(0);
@@ -72,6 +74,8 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
connect(views_.first()->view()->horizontalScrollBar(), &QScrollBar::rangeChanged, scrollbar(), &QScrollBar::setRange);
vert_layout->addWidget(scrollbar());
connect(ruler(), &TimeRuler::TimeChanged, this, &TimelineWidget::SetViewTimestamp);
foreach (TimelineAndTrackView* tview, views_) {
TimelineView* view = tview->view();
@@ -82,9 +86,7 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
connect(view->horizontalScrollBar(), &QScrollBar::valueChanged, ruler(), &TimeRuler::SetScroll);
connect(view, &TimelineView::ScaleChanged, this, &TimelineWidget::SetScale);
connect(ruler(), &TimeRuler::TimeChanged, view, &TimelineView::SetTime);
connect(view, &TimelineView::TimeChanged, ruler(), &TimeRuler::SetTime);
connect(view, &TimelineView::TimeChanged, this, &TimelineWidget::TimeChanged);
connect(view, &TimelineView::TimeChanged, this, &TimelineWidget::ViewTimestampChanged);
connect(view, &TimelineView::customContextMenuRequested, this, &TimelineWidget::ShowContextMenu);
connect(scrollbar(), &QScrollBar::valueChanged, view->horizontalScrollBar(), &QScrollBar::setValue);
connect(view->horizontalScrollBar(), &QScrollBar::valueChanged, scrollbar(), &QScrollBar::setValue);
@@ -158,9 +160,7 @@ void TimelineWidget::TimebaseChangedEvent(const rational &timebase)
}
}
foreach (TimelineAndTrackView* view, views_) {
view->view()->SetTimebase(timebase);
}
UpdateViewTimebases();
}
void TimelineWidget::resizeEvent(QResizeEvent *event)
@@ -173,9 +173,7 @@ void TimelineWidget::resizeEvent(QResizeEvent *event)
void TimelineWidget::TimeChangedEvent(const int64_t& timestamp)
{
foreach (TimelineAndTrackView* view, views_) {
view->view()->SetTime(timestamp);
}
SetViewTimestamp(timestamp);
timecode_label_->SetValue(timestamp);
}
@@ -333,6 +331,14 @@ QList<TimelineWidget::DraggedFootage> TimelineWidget::FootageToDraggedFootage(QL
return df;
}
rational TimelineWidget::GetToolTipTimebase() const
{
if (GetConnectedNode() && use_audio_time_units_) {
return GetConnectedNode()->audio_params().time_base();
}
return timebase();
}
void TimelineWidget::SelectAll()
{
foreach (TimelineAndTrackView* view, views_) {
@@ -1042,8 +1048,20 @@ void TimelineWidget::ShowContextMenu()
if (!selected.isEmpty()) {
QAction* speed_duration_action = menu.addAction(tr("Speed/Duration"));
connect(speed_duration_action, &QAction::triggered, this, &TimelineWidget::ShowSpeedDurationDialog);
menu.addSeparator();
}
QAction* toggle_audio_units = menu.addAction(tr("Use Audio Time Units"));
toggle_audio_units->setCheckable(true);
toggle_audio_units->setChecked(use_audio_time_units_);
connect(toggle_audio_units, &QAction::triggered, this, &TimelineWidget::SetUseAudioTimeUnits);
menu.addSeparator();
QAction* properties_action = menu.addAction(tr("Properties"));
connect(properties_action, &QAction::triggered, this, &TimelineWidget::ShowSequenceDialog);
menu.exec(QCursor::pos());
}
@@ -1072,6 +1090,57 @@ void TimelineWidget::DeferredScrollAction()
scrollbar()->setValue(deferred_scroll_value_);
}
void TimelineWidget::ShowSequenceDialog()
{
if (!GetConnectedNode()) {
return;
}
SequenceDialog sd(static_cast<Sequence*>(GetConnectedNode()->parent()), SequenceDialog::kExisting, this);
sd.exec();
}
void TimelineWidget::SetUseAudioTimeUnits(bool use)
{
use_audio_time_units_ = use;
// Update timebases
UpdateViewTimebases();
// Force update of the viewer timestamps
SetViewTimestamp(GetTimestamp());
}
void TimelineWidget::SetViewTimestamp(const int64_t &ts)
{
for (int i=0;i<views_.size();i++) {
TimelineAndTrackView* view = views_.at(i);
if (use_audio_time_units_ && i == Timeline::kTrackTypeAudio) {
view->view()->SetTime(Timecode::rescale_timestamp(ts,
timebase(),
GetConnectedNode()->audio_params().time_base()));
} else {
view->view()->SetTime(ts);
}
}
}
void TimelineWidget::ViewTimestampChanged(int64_t ts)
{
if (use_audio_time_units_ && sender() == views_.at(Timeline::kTrackTypeAudio)) {
ts = Timecode::rescale_timestamp(ts,
GetConnectedNode()->audio_params().time_base(),
timebase());
}
// Update all other views
SetViewTimestamp(ts);
ruler()->SetTime(ts);
emit TimeChanged(ts);
}
void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost)
{
ghost->SetScale(GetScale());
@@ -1079,6 +1148,19 @@ void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost)
views_.at(ghost->Track().type())->view()->scene()->addItem(ghost);
}
void TimelineWidget::UpdateViewTimebases()
{
for (int i=0;i<views_.size();i++) {
TimelineAndTrackView* view = views_.at(i);
if (use_audio_time_units_ && i == Timeline::kTrackTypeAudio) {
view->view()->SetTimebase(GetConnectedNode()->audio_params().time_base());
} else {
view->view()->SetTimebase(timebase());
}
}
}
void TimelineWidget::SetBlockLinksSelected(Block* block, bool selected)
{
TimelineViewBlockItem* link_item;
@@ -391,6 +391,8 @@ private:
bool dual_transition_;
};
rational GetToolTipTimebase() const;
void InsertGapsAt(const rational& time, const rational& length, QUndoCommand* command);
void DeleteSelectedInternal(const QList<Block *>& blocks, bool transition_aware, bool remove_from_graph, QUndoCommand* command);
@@ -431,6 +433,8 @@ private:
int deferred_scroll_value_;
bool use_audio_time_units_;
int GetTrackY(const TrackReference& ref);
int GetTrackHeight(const TrackReference& ref);
@@ -438,6 +442,8 @@ private:
void AddGhost(TimelineViewGhostItem* ghost);
void UpdateViewTimebases();
private slots:
void UpdateTimelineLength(const rational& length);
@@ -481,6 +487,14 @@ private slots:
void DeferredScrollAction();
void ShowSequenceDialog();
void SetUseAudioTimeUnits(bool use);
void SetViewTimestamp(const int64_t& ts);
void ViewTimestampChanged(int64_t ts);
};
#endif // TIMELINEWIDGET_H
+4 -7
View File
@@ -142,9 +142,9 @@ void TimelineWidget::ImportTool::DragMove(TimelineViewMouseEvent *event)
}
// Generate tooltip (showing earliest in point of imported clip)
int64_t earliest_timestamp = Timecode::time_to_timestamp(earliest_ghost, parent()->timebase());
int64_t earliest_timestamp = Timecode::time_to_timestamp(earliest_ghost, parent()->GetToolTipTimebase());
QString tooltip_text = Timecode::timestamp_to_timecode(earliest_timestamp,
parent()->timebase(),
parent()->GetToolTipTimebase(),
Core::instance()->GetTimecodeDisplay());
// Force tooltip to update (otherwise the tooltip won't move as written in the documentation, and could get in the way
@@ -232,11 +232,8 @@ void TimelineWidget::ImportTool::FootageToGhosts(rational ghost_start, const QLi
// Stream is essentially length-less - use config's default image length
footage_duration = Config::Current()["DefaultStillLength"].value<rational>();
} else {
// Use duration from file
int64_t stream_duration = stream->duration();
// Rescale to timeline timebase
stream_duration = qCeil(static_cast<double>(stream_duration) * stream->timebase().toDouble() / dest_tb.toDouble());
// Rescale stream duration to timeline timebase
int64_t stream_duration = Timecode::rescale_timestamp_ceil(stream->duration(), stream->timebase(), dest_tb);
// Convert to rational time
footage_duration = rational(dest_tb.numerator() * stream_duration,
+2 -2
View File
@@ -401,9 +401,9 @@ void TimelineWidget::PointerTool::ProcessDrag(const TimelineCoordinate &mouse_po
// Show tooltip
// Generate tooltip (showing earliest in point of imported clip)
int64_t earliest_timestamp = Timecode::time_to_timestamp(time_movement, parent()->timebase());
int64_t earliest_timestamp = Timecode::time_to_timestamp(time_movement, parent()->GetToolTipTimebase());
QString tooltip_text = Timecode::timestamp_to_timecode(earliest_timestamp,
parent()->timebase(),
parent()->GetToolTipTimebase(),
Core::instance()->GetTimecodeDisplay(),
true);
+2 -2
View File
@@ -51,9 +51,9 @@ void TimelineWidget::SlipTool::ProcessDrag(const TimelineCoordinate &mouse_pos)
// Show tooltip
// Generate tooltip (showing earliest in point of imported clip)
int64_t earliest_timestamp = Timecode::time_to_timestamp(time_movement, parent()->timebase());
int64_t earliest_timestamp = Timecode::time_to_timestamp(time_movement, parent()->GetToolTipTimebase());
QString tooltip_text = Timecode::timestamp_to_timecode(earliest_timestamp,
parent()->timebase(),
parent()->GetToolTipTimebase(),
Core::instance()->GetTimecodeDisplay(),
true);
// Force tooltip to update (otherwise the tooltip won't move as written in the documentation, and could get in the way