timeline: snap pointer and import operations to timebase

If frame rates are mixed, ensures that video doesn't end up falling or ending on half frames which potentially make the entire sequence awkward
This commit is contained in:
itsmattkc
2020-11-25 11:46:41 +11:00
parent c35005d1bf
commit b5cc13c10e
10 changed files with 75 additions and 34 deletions
+5 -8
View File
@@ -353,14 +353,6 @@ void TimelineWidget::PasteNodesFromClipboardInternal(QXmlStreamReader *reader, X
}
}
rational TimelineWidget::GetToolTipTimebase() const
{
if (GetConnectedNode() && use_audio_time_units_) {
return GetConnectedNode()->audio_params().time_base();
}
return timebase();
}
void TimelineWidget::SelectAll()
{
QVector<Block*> newly_selected_blocks;
@@ -1211,6 +1203,11 @@ TimelineView *TimelineWidget::GetFirstTimelineView()
return views_.first()->view();
}
rational TimelineWidget::GetTimebaseForTrackType(Timeline::TrackType type)
{
return views_.at(type)->view()->timebase();
}
const QRect& TimelineWidget::GetRubberBandGeometry() const
{
return rubberband_.geometry();
+2 -2
View File
@@ -154,8 +154,6 @@ public:
return !ghost_items_.isEmpty();
}
rational GetToolTipTimebase() const;
bool IsBlockSelected(Block* b) const
{
return selected_blocks_.contains(b);
@@ -167,6 +165,8 @@ public:
TimelineView* GetFirstTimelineView();
rational GetTimebaseForTrackType(Timeline::TrackType type);
const QRect &GetRubberBandGeometry() const;
/**
+9 -2
View File
@@ -149,9 +149,10 @@ void ImportTool::DragMove(TimelineViewMouseEvent *event)
}
// Generate tooltip (showing earliest in point of imported clip)
int64_t earliest_timestamp = Timecode::time_to_timestamp(earliest_ghost, parent()->GetToolTipTimebase());
rational tooltip_timebase = parent()->GetTimebaseForTrackType(event->GetTrack().type());
int64_t earliest_timestamp = Timecode::time_to_timestamp(earliest_ghost, tooltip_timebase);
QString tooltip_text = Timecode::timestamp_to_timecode(earliest_timestamp,
parent()->GetToolTipTimebase(),
tooltip_timebase,
Core::instance()->GetTimecodeDisplay());
// Force tooltip to update (otherwise the tooltip won't move as written in the documentation, and could get in the way
@@ -271,6 +272,12 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QList<DraggedFootag
footage_duration = Config::Current()["DefaultStillLength"].value<rational>();
}
// Snap footage duration to timebase
rational snap_mvmt = SnapMovementToTimebase(footage_duration, 0, dest_tb);
if (!snap_mvmt.isNull()) {
footage_duration += snap_mvmt;
}
foreach (TimelineViewGhostItem* ghost, footage_ghosts) {
ghost->SetIn(ghost_start);
ghost->SetOut(ghost_start + footage_duration);
+25 -5
View File
@@ -469,7 +469,7 @@ void PointerTool::ProcessDrag(const TimelineCoordinate &mouse_pos)
}
// Validate ghosts that are being moved (clips from other track types do NOT get moved)
{
if (track_movement != 0) {
QVector<TimelineViewGhostItem*> validate_track_ghosts = parent()->GetGhostItems();
for (int i=0;i<validate_track_ghosts.size();i++) {
if (validate_track_ghosts.at(i)->GetTrack().type() != drag_track_type_) {
@@ -508,10 +508,11 @@ void PointerTool::ProcessDrag(const TimelineCoordinate &mouse_pos)
// Regenerate tooltip and force it to update (otherwise the tooltip won't move as written in the
// documentation, and could get in the way of the cursor)
rational tooltip_timebase = parent()->GetTimebaseForTrackType(drag_start_.GetTrack().type());
QToolTip::hideText();
QToolTip::showText(QCursor::pos(),
Timecode::timestamp_to_timecode(Timecode::time_to_timestamp(time_movement, parent()->GetToolTipTimebase()),
parent()->GetToolTipTimebase(),
Timecode::timestamp_to_timecode(Timecode::time_to_timestamp(time_movement, tooltip_timebase),
tooltip_timebase,
Core::instance()->GetTimecodeDisplay(),
true),
parent());
@@ -872,6 +873,8 @@ bool PointerTool::AddMovingTransitionsToClipGhost(Block* block,
rational PointerTool::ValidateInTrimming(rational movement)
{
bool first_ghost = true;
foreach (TimelineViewGhostItem* ghost, parent()->GetGhostItems()) {
if (ghost->GetMode() != Timeline::kTrimIn) {
continue;
@@ -880,8 +883,11 @@ rational PointerTool::ValidateInTrimming(rational movement)
rational earliest_in = RATIONAL_MIN;
rational latest_in = ghost->GetOut();
rational ghost_timebase = parent()->GetTimebaseForTrackType(ghost->GetTrack().type());
// If the ghost must be at least one frame in size, limit the latest allowed in point
if (!ghost->CanHaveZeroLength()) {
latest_in -= parent()->timebase();
latest_in -= ghost_timebase;
}
// Clamp adjusted value between the earliest and latest values
@@ -891,6 +897,11 @@ rational PointerTool::ValidateInTrimming(rational movement)
if (clamped != adjusted) {
movement = clamped - ghost->GetIn();
}
if (first_ghost) {
movement = SnapMovementToTimebase(ghost->GetIn(), movement, ghost_timebase);
first_ghost = false;
}
}
return movement;
@@ -898,6 +909,8 @@ rational PointerTool::ValidateInTrimming(rational movement)
rational PointerTool::ValidateOutTrimming(rational movement)
{
bool first_ghost = true;
foreach (TimelineViewGhostItem* ghost, parent()->GetGhostItems()) {
if (ghost->GetMode() != Timeline::kTrimOut) {
continue;
@@ -906,8 +919,10 @@ rational PointerTool::ValidateOutTrimming(rational movement)
// Determine earliest and latest out points
rational earliest_out = ghost->GetIn();
rational ghost_timebase = parent()->GetTimebaseForTrackType(ghost->GetTrack().type());
if (!ghost->CanHaveZeroLength()) {
earliest_out += parent()->timebase();
earliest_out += ghost_timebase;
}
rational latest_out = RATIONAL_MAX;
@@ -919,6 +934,11 @@ rational PointerTool::ValidateOutTrimming(rational movement)
if (clamped != adjusted) {
movement = clamped - ghost->GetOut();
}
if (first_ghost) {
movement = SnapMovementToTimebase(ghost->GetOut(), movement, ghost_timebase);
first_ghost = false;
}
}
return movement;
+3 -2
View File
@@ -54,10 +54,11 @@ void SlipTool::ProcessDrag(const TimelineCoordinate &mouse_pos)
// Generate tooltip and force it to to update (otherwise the tooltip won't move as written in the
// documentation, and could get in the way of the cursor)
rational tooltip_timebase = parent()->GetTimebaseForTrackType(drag_start_.GetTrack().type());
QToolTip::hideText();
QToolTip::showText(QCursor::pos(),
Timecode::timestamp_to_timecode(Timecode::time_to_timestamp(time_movement, parent()->GetToolTipTimebase()),
parent()->GetToolTipTimebase(),
Timecode::timestamp_to_timecode(Timecode::time_to_timestamp(time_movement, tooltip_timebase),
tooltip_timebase,
Core::instance()->GetTimecodeDisplay(),
true),
parent());
+19
View File
@@ -53,8 +53,22 @@ Timeline::MovementMode TimelineTool::FlipTrimMode(const Timeline::MovementMode &
return trim_mode;
}
rational TimelineTool::SnapMovementToTimebase(const rational &start, rational movement, const rational &timebase)
{
rational proposed_position = start + movement;
rational snapped = Timecode::snap_time_to_timebase(proposed_position, timebase);
if (proposed_position != snapped) {
movement += snapped - proposed_position;
}
return movement;
}
rational TimelineTool::ValidateTimeMovement(rational movement)
{
bool first_ghost = true;
foreach (TimelineViewGhostItem* ghost, parent()->GetGhostItems()) {
if (ghost->GetMode() != Timeline::kMove) {
continue;
@@ -63,6 +77,11 @@ rational TimelineTool::ValidateTimeMovement(rational movement)
// Prevents any ghosts from going below 0:00:00 time
if (ghost->GetIn() + movement < 0) {
movement = -ghost->GetIn();
} else if (first_ghost) {
// Ensure ghost is snapped to a grid
movement = SnapMovementToTimebase(ghost->GetIn(), movement, parent()->GetTimebaseForTrackType(ghost->GetTrack().type()));
first_ghost = false;
}
}
+2
View File
@@ -53,6 +53,8 @@ public:
static Timeline::MovementMode FlipTrimMode(const Timeline::MovementMode& trim_mode);
static rational SnapMovementToTimebase(const rational& start, rational movement, const rational& timebase);
protected:
/**
* @brief Validates Ghosts that are moving horizontally (time-based)
+6 -1
View File
@@ -165,6 +165,7 @@ TrackRippleRemoveAreaCommand::TrackRippleRemoveAreaCommand(TrackOutput *track, r
in_(in),
out_(out),
splice_(false),
splice_split_command_(nullptr),
trim_out_(nullptr),
trim_in_(nullptr),
insert_(nullptr)
@@ -305,7 +306,6 @@ void TrackRippleRemoveAreaCommand::undo_internal()
trim_out_->set_length_and_media_out(trim_out_old_length_);
splice_split_command_->undo();
delete splice_split_command_;
} else {
@@ -344,6 +344,11 @@ void TrackRippleRemoveAreaCommand::undo_internal()
track_->Node::InvalidateCache(TimeRange(in_, insert_ ? out_ : RATIONAL_MAX),
track_->block_input(), track_->block_input());
if (splice_split_command_) {
delete splice_split_command_;
splice_split_command_ = nullptr;
}
}
TrackPlaceBlockCommand::TrackPlaceBlockCommand(TrackList *timeline, int track, Block *block, rational in, QUndoCommand *parent) :
@@ -115,13 +115,13 @@ void TimelineView::wheelEvent(QWheelEvent *event)
return;
} else {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
QPoint angle_delta = event->angleDelta();
if (Config::Current()["InvertTimelineScrollAxes"].toBool()) {
angle_delta = QPoint(angle_delta.y(), angle_delta.x());
}
QWheelEvent e(
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
event->position(),
@@ -255,7 +255,8 @@ void TimelineView::drawForeground(QPainter *painter, const QRectF &rect)
painter->setBrush(Qt::NoBrush);
foreach (TimelineViewGhostItem* ghost, (*ghosts_)) {
if (ghost->GetTrack().type() == connected_track_list_->type()) {
if (ghost->GetTrack().type() == connected_track_list_->type()
&& !ghost->IsInvisible()) {
int track_index = ghost->GetAdjustedTrack().index();
painter->drawRect(TimeToScene(ghost->GetAdjustedIn()),
@@ -74,17 +74,6 @@ void TimelineViewGhostItem::SetCanMoveTracks(bool e)
can_move_tracks_ = e;
}
/*void TimelineViewGhostItem::SetInvisible(bool invisible)
{
setBrush(Qt::NoBrush);
if (invisible) {
setPen(Qt::NoPen);
} else {
setPen(QPen(Qt::yellow, 2)); // FIXME: Make customizable via CSS
}
}*/
const rational &TimelineViewGhostItem::GetIn() const
{
return in_;