timeline: vastly improved snapping
This commit is contained in:
@@ -21,6 +21,8 @@ add_subdirectory(view)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
widget/timelinewidget/snapservice.h
|
||||
widget/timelinewidget/snapservice.cpp
|
||||
widget/timelinewidget/timelineandtrackview.h
|
||||
widget/timelinewidget/timelineandtrackview.cpp
|
||||
widget/timelinewidget/timelinescaledobject.h
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
#include "snapservice.h"
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef SNAPSERVICE_H
|
||||
#define SNAPSERVICE_H
|
||||
|
||||
#include "common/rational.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class SnapService
|
||||
{
|
||||
public:
|
||||
SnapService() = default;
|
||||
|
||||
enum SnapPoints {
|
||||
kSnapToClips = 0x1,
|
||||
kSnapToPlayhead = 0x2,
|
||||
kSnapToMarkers = 0x4,
|
||||
kSnapAll = 0xFF
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Snaps point `start_point` that is moving by `movement` to currently existing clips
|
||||
*/
|
||||
virtual bool SnapPoint(QList<rational> start_times, rational *movement, int snap_points = kSnapAll) = 0;
|
||||
|
||||
virtual void HideSnaps() = 0;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // SNAPSERVICE_H
|
||||
@@ -20,11 +20,13 @@
|
||||
|
||||
#include "timelinewidget.h"
|
||||
|
||||
#include <cfloat>
|
||||
#include <QSplitter>
|
||||
#include <QVBoxLayout>
|
||||
#include <QtMath>
|
||||
|
||||
#include "core.h"
|
||||
#include "common/range.h"
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "dialog/sequence/sequence.h"
|
||||
#include "dialog/speedduration/speedduration.h"
|
||||
@@ -56,6 +58,7 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
|
||||
ruler_and_time_layout->addWidget(timecode_label_);
|
||||
|
||||
ruler_and_time_layout->addWidget(ruler());
|
||||
ruler()->SetSnapService(this);
|
||||
|
||||
// Create list of TimelineViews - these MUST correspond to the ViewType enum
|
||||
|
||||
@@ -103,6 +106,7 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
|
||||
|
||||
view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||
view->SetSnapService(this);
|
||||
|
||||
view_splitter->addWidget(tview);
|
||||
|
||||
@@ -906,6 +910,8 @@ void TimelineWidget::ClearGhosts()
|
||||
|
||||
ghost_items_.clear();
|
||||
}
|
||||
|
||||
HideSnaps();
|
||||
}
|
||||
|
||||
bool TimelineWidget::HasGhosts()
|
||||
@@ -1266,7 +1272,7 @@ void TimelineWidget::SetBlockLinksSelected(Block* block, bool selected)
|
||||
}
|
||||
|
||||
QVector<Timeline::EditToInfo> TimelineWidget::GetEditToInfo(const rational& playhead_time,
|
||||
Timeline::MovementMode mode)
|
||||
Timeline::MovementMode mode)
|
||||
{
|
||||
// Get list of unlocked tracks
|
||||
QVector<TrackOutput*> tracks = GetConnectedNode()->GetUnlockedTracks();
|
||||
@@ -1399,6 +1405,20 @@ void TimelineWidget::EditTo(Timeline::MovementMode mode)
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
void TimelineWidget::ShowSnap(const QList<rational> ×)
|
||||
{
|
||||
foreach (TimelineAndTrackView* tview, views_) {
|
||||
tview->view()->EnableSnap(times);
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::HideSnaps()
|
||||
{
|
||||
foreach (TimelineAndTrackView* tview, views_) {
|
||||
tview->view()->DisableSnap();
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::StartRubberBandSelect(bool enable_selecting, bool select_links)
|
||||
{
|
||||
drag_origin_ = QCursor::pos();
|
||||
@@ -1478,4 +1498,107 @@ void TimelineWidget::EndRubberBandSelect(bool enable_selecting, bool select_link
|
||||
ViewSelectionChanged();
|
||||
}
|
||||
|
||||
struct SnapData {
|
||||
rational time;
|
||||
rational movement;
|
||||
};
|
||||
|
||||
QList<SnapData> AttemptSnap(const QList<double>& screen_pt,
|
||||
double compare_pt,
|
||||
const QList<rational>& start_times,
|
||||
const rational& compare_time) {
|
||||
const qreal kSnapRange = 10; // FIXME: Hardcoded number
|
||||
|
||||
QList<SnapData> snap_data;
|
||||
|
||||
for (int i=0;i<screen_pt.size();i++) {
|
||||
// Attempt snapping to clip out point
|
||||
if (InRange(screen_pt.at(i), compare_pt, kSnapRange)) {
|
||||
snap_data.append({compare_time, compare_time - start_times.at(i)});
|
||||
}
|
||||
}
|
||||
|
||||
return snap_data;
|
||||
}
|
||||
|
||||
bool TimelineWidget::SnapPoint(QList<rational> start_times, rational* movement, int snap_points)
|
||||
{
|
||||
QList<double> screen_pt;
|
||||
|
||||
foreach (const rational& s, start_times) {
|
||||
screen_pt.append(TimeToScene(s + *movement));
|
||||
}
|
||||
|
||||
QList<SnapData> potential_snaps;
|
||||
|
||||
if (snap_points & kSnapToPlayhead) {
|
||||
rational playhead_abs_time = GetTime();
|
||||
qreal playhead_pos = TimeToScene(playhead_abs_time);
|
||||
potential_snaps.append(AttemptSnap(screen_pt, playhead_pos, start_times, playhead_abs_time));
|
||||
}
|
||||
|
||||
if (snap_points & kSnapToClips) {
|
||||
QMap<Block*, TimelineViewBlockItem*>::const_iterator i;
|
||||
|
||||
for (i=block_items_.constBegin(); i!=block_items_.constEnd(); i++) {
|
||||
TimelineViewBlockItem* item = i.value();
|
||||
|
||||
if (item) {
|
||||
qreal rect_left = item->x();
|
||||
qreal rect_right = rect_left + item->rect().width();
|
||||
|
||||
// Attempt snapping to clip in point
|
||||
potential_snaps.append(AttemptSnap(screen_pt, rect_left, start_times, item->block()->in()));
|
||||
|
||||
// Attempt snapping to clip out point
|
||||
potential_snaps.append(AttemptSnap(screen_pt, rect_right, start_times, item->block()->out()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((snap_points & kSnapToMarkers) && GetConnectedTimelinePoints()) {
|
||||
foreach (TimelineMarker* m, GetConnectedTimelinePoints()->markers()->list()) {
|
||||
qreal marker_pos = TimeToScene(m->time().in());
|
||||
potential_snaps.append(AttemptSnap(screen_pt, marker_pos, start_times, m->time().in()));
|
||||
|
||||
if (m->time().in() != m->time().out()) {
|
||||
marker_pos = TimeToScene(m->time().out());
|
||||
potential_snaps.append(AttemptSnap(screen_pt, marker_pos, start_times, m->time().out()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (potential_snaps.isEmpty()) {
|
||||
HideSnaps();
|
||||
return false;
|
||||
}
|
||||
|
||||
int closest_snap = 0;
|
||||
rational closest_diff = qAbs(potential_snaps.at(0).movement - *movement);
|
||||
|
||||
// Determine which snap point was the closest
|
||||
for (int i=1; i<potential_snaps.size(); i++) {
|
||||
rational this_diff = qAbs(potential_snaps.at(i).movement - *movement);
|
||||
|
||||
if (this_diff < closest_diff) {
|
||||
closest_snap = i;
|
||||
closest_diff = this_diff;
|
||||
}
|
||||
}
|
||||
|
||||
*movement = potential_snaps.at(closest_snap).movement;
|
||||
|
||||
// Find all points at this movement
|
||||
QList<rational> snap_times;
|
||||
foreach (const SnapData& data, potential_snaps) {
|
||||
if (data.movement == *movement) {
|
||||
snap_times.append(data.time);
|
||||
}
|
||||
}
|
||||
|
||||
ShowSnap(snap_times);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "snapservice.h"
|
||||
#include "timeline/timelinecommon.h"
|
||||
#include "timelineandtrackview.h"
|
||||
#include "widget/nodecopypaste/nodecopypaste.h"
|
||||
@@ -40,7 +41,7 @@ OLIVE_NAMESPACE_ENTER
|
||||
*
|
||||
* Encapsulates TimelineViews, TimeRulers, and scrollbars for a complete widget to manipulate Timelines
|
||||
*/
|
||||
class TimelineWidget : public TimeBasedWidget, public NodeCopyPasteWidget
|
||||
class TimelineWidget : public TimeBasedWidget, public NodeCopyPasteWidget, public SnapService
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -93,6 +94,10 @@ public:
|
||||
|
||||
QList<TimelineViewBlockItem*> GetSelectedBlocks();
|
||||
|
||||
virtual bool SnapPoint(QList<rational> start_times, rational *movement, int snap_points = kSnapAll) override;
|
||||
|
||||
virtual void HideSnaps() override;
|
||||
|
||||
signals:
|
||||
void SelectionChanged(const QList<Block*>& selected_blocks);
|
||||
|
||||
@@ -189,17 +194,6 @@ private:
|
||||
*/
|
||||
int ValidateTrackMovement(int movement, const QVector<TimelineViewGhostItem*> ghosts);
|
||||
|
||||
enum SnapPoints {
|
||||
kSnapToClips = 0x1,
|
||||
kSnapToPlayhead = 0x2,
|
||||
kSnapAll = 0xFF
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Snaps point `start_point` that is moving by `movement` to currently existing clips
|
||||
*/
|
||||
bool SnapPoint(QList<rational> start_times, rational *movement, int snap_points = kSnapAll);
|
||||
|
||||
void GetGhostData(const QVector<TimelineViewGhostItem*>& ghosts, rational *earliest_point, rational *latest_point);
|
||||
|
||||
void InsertGapsAtGhostDestination(const QVector<TimelineViewGhostItem*>& ghosts, QUndoCommand* command);
|
||||
@@ -455,6 +449,8 @@ private:
|
||||
|
||||
void EditTo(Timeline::MovementMode mode);
|
||||
|
||||
void ShowSnap(const QList<rational>& times);
|
||||
|
||||
QPoint drag_origin_;
|
||||
|
||||
void StartRubberBandSelect(bool enable_selecting, bool select_links);
|
||||
|
||||
@@ -150,13 +150,13 @@ void TimelineWidget::AddTool::MouseMoveInternal(const rational &cursor_frame, bo
|
||||
rational movement = cursor_frame - drag_start_point_;
|
||||
|
||||
// Snap movement
|
||||
bool snapped = SnapPoint(snap_points_, &movement);
|
||||
bool snapped = parent()->SnapPoint(snap_points_, &movement);
|
||||
|
||||
// If alt is held, our movement goes both ways (outwards)
|
||||
if (!snapped && outwards) {
|
||||
// Snap backwards too
|
||||
movement = -movement;
|
||||
SnapPoint(snap_points_, &movement);
|
||||
parent()->SnapPoint(snap_points_, &movement);
|
||||
// We don't need to un-neg here because outwards means all future processing will be done both pos and neg
|
||||
}
|
||||
|
||||
|
||||
@@ -122,14 +122,17 @@ void TimelineWidget::ImportTool::DragMove(TimelineViewMouseEvent *event)
|
||||
rational time_movement = event->GetFrame() - drag_start_.GetFrame();
|
||||
int track_movement = event->GetTrack().index() - drag_start_.GetTrack().index();
|
||||
|
||||
// If snapping is enabled, check for snap points
|
||||
if (Core::instance()->snapping()) {
|
||||
SnapPoint(snap_points_, &time_movement);
|
||||
}
|
||||
|
||||
time_movement = ValidateTimeMovement(time_movement, parent()->ghost_items_);
|
||||
track_movement = ValidateTrackMovement(track_movement, parent()->ghost_items_);
|
||||
|
||||
// If snapping is enabled, check for snap points
|
||||
if (Core::instance()->snapping()) {
|
||||
parent()->SnapPoint(snap_points_, &time_movement);
|
||||
|
||||
time_movement = ValidateTimeMovement(time_movement, parent()->ghost_items_);
|
||||
track_movement = ValidateTrackMovement(track_movement, parent()->ghost_items_);
|
||||
}
|
||||
|
||||
rational earliest_ghost = RATIONAL_MAX;
|
||||
|
||||
// Move ghosts to the mouse cursor
|
||||
|
||||
@@ -314,17 +314,20 @@ void TimelineWidget::PointerTool::ProcessDrag(const TimelineCoordinate &mouse_po
|
||||
// Determine frame movement
|
||||
rational time_movement = mouse_pos.GetFrame() - drag_start_.GetFrame();
|
||||
|
||||
// Perform snapping if enabled (adjusts time_movement if it's close to any potential snap points)
|
||||
if (Core::instance()->snapping()) {
|
||||
SnapPoint(snap_points_, &time_movement);
|
||||
}
|
||||
|
||||
// Validate movement (enforce all ghosts moving in legal ways)
|
||||
// NOTE: Always do this after snapping to ensure the snap hasn't made an illegal movement.
|
||||
time_movement = ValidateTimeMovement(time_movement, parent()->ghost_items_);
|
||||
time_movement = ValidateInTrimming(time_movement, parent()->ghost_items_, !trim_overwrite_allowed_);
|
||||
time_movement = ValidateOutTrimming(time_movement, parent()->ghost_items_, !trim_overwrite_allowed_);
|
||||
|
||||
// Perform snapping if enabled (adjusts time_movement if it's close to any potential snap points)
|
||||
if (Core::instance()->snapping()) {
|
||||
parent()->SnapPoint(snap_points_, &time_movement);
|
||||
|
||||
time_movement = ValidateTimeMovement(time_movement, parent()->ghost_items_);
|
||||
time_movement = ValidateInTrimming(time_movement, parent()->ghost_items_, !trim_overwrite_allowed_);
|
||||
time_movement = ValidateOutTrimming(time_movement, parent()->ghost_items_, !trim_overwrite_allowed_);
|
||||
}
|
||||
|
||||
// Validate ghosts that are being moved (clips from other track types do NOT get moved)
|
||||
{
|
||||
QVector<TimelineViewGhostItem*> validate_track_ghosts = parent()->ghost_items_;
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
|
||||
#include "widget/timelinewidget/timelinewidget.h"
|
||||
|
||||
#include <cfloat>
|
||||
|
||||
#include "common/range.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
|
||||
@@ -76,28 +73,6 @@ TimelineViewBlockItem *TimelineWidget::Tool::GetItemAtScenePos(const TimelineCoo
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AttemptSnap(const QList<double>& proposed_pts,
|
||||
double compare_point,
|
||||
const QList<rational>& start_times,
|
||||
rational compare_time,
|
||||
rational* movement,
|
||||
double* diff) {
|
||||
const qreal kSnapRange = 10; // FIXME: Hardcoded number
|
||||
|
||||
for (int i=0;i<proposed_pts.size();i++) {
|
||||
// Attempt snapping to clip out point
|
||||
if (InRange(proposed_pts.at(i), compare_point, kSnapRange)) {
|
||||
double this_diff = qAbs(compare_point - proposed_pts.at(i));
|
||||
|
||||
if (this_diff < *diff
|
||||
&& start_times.at(i) + *movement >= 0) {
|
||||
*movement = compare_time - start_times.at(i);
|
||||
*diff = this_diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rational TimelineWidget::Tool::ValidateTimeMovement(rational movement, const QVector<TimelineViewGhostItem *> ghosts)
|
||||
{
|
||||
foreach (TimelineViewGhostItem* ghost, ghosts) {
|
||||
@@ -153,52 +128,6 @@ int TimelineWidget::Tool::ValidateTrackMovement(int movement, const QVector<Time
|
||||
return movement;
|
||||
}
|
||||
|
||||
bool TimelineWidget::Tool::SnapPoint(QList<rational> start_times, rational* movement, int snap_points)
|
||||
{
|
||||
double diff = DBL_MAX;
|
||||
|
||||
QList<double> proposed_pts;
|
||||
|
||||
foreach (rational s, start_times) {
|
||||
proposed_pts.append((s + *movement).toDouble() * parent()->GetScale());
|
||||
}
|
||||
|
||||
if (snap_points & kSnapToPlayhead) {
|
||||
|
||||
|
||||
rational playhead_abs_time = rational(parent()->GetTimestamp() * parent()->timebase().numerator(),
|
||||
parent()->timebase().denominator());
|
||||
|
||||
qreal playhead_pos = playhead_abs_time.toDouble() * parent()->GetScale();
|
||||
|
||||
AttemptSnap(proposed_pts, playhead_pos, start_times, playhead_abs_time, movement, &diff);
|
||||
}
|
||||
|
||||
if (snap_points & kSnapToClips) {
|
||||
QMapIterator<Block*, TimelineViewBlockItem*> iterator(parent()->block_items_);
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next();
|
||||
|
||||
TimelineViewBlockItem* item = iterator.value();
|
||||
|
||||
if (item != nullptr) {
|
||||
qreal rect_left = item->x();
|
||||
qreal rect_right = rect_left + item->rect().width();
|
||||
|
||||
// Attempt snapping to clip in point
|
||||
AttemptSnap(proposed_pts, rect_left, start_times, item->block()->in(), movement, &diff);
|
||||
|
||||
// Attempt snapping to clip out point
|
||||
AttemptSnap(proposed_pts, rect_right, start_times, item->block()->out(), movement, &diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (diff < DBL_MAX);
|
||||
}
|
||||
|
||||
void TimelineWidget::Tool::GetGhostData(const QVector<TimelineViewGhostItem *> &ghosts, rational *earliest_point, rational *latest_point)
|
||||
{
|
||||
rational ep = RATIONAL_MAX;
|
||||
|
||||
@@ -39,7 +39,9 @@ TimelineViewBase::TimelineViewBase(QWidget *parent) :
|
||||
playhead_scene_left_(-1),
|
||||
playhead_scene_right_(-1),
|
||||
dragging_playhead_(false),
|
||||
limit_y_axis_(false)
|
||||
limit_y_axis_(false),
|
||||
snapped_(false),
|
||||
snap_service_(nullptr)
|
||||
{
|
||||
setScene(&scene_);
|
||||
|
||||
@@ -59,6 +61,26 @@ void TimelineViewBase::TimebaseChangedEvent(const rational &)
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
void TimelineViewBase::EnableSnap(const QList<rational> &points)
|
||||
{
|
||||
snapped_ = true;
|
||||
snap_time_ = points;
|
||||
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
void TimelineViewBase::DisableSnap()
|
||||
{
|
||||
snapped_ = false;
|
||||
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
void TimelineViewBase::SetSnapService(SnapService *service)
|
||||
{
|
||||
snap_service_ = service;
|
||||
}
|
||||
|
||||
void TimelineViewBase::SetTime(const int64_t time)
|
||||
{
|
||||
playhead_ = time;
|
||||
@@ -91,6 +113,16 @@ void TimelineViewBase::drawForeground(QPainter *painter, const QRectF &rect)
|
||||
|
||||
playhead_style_.Draw(painter, QRectF(playhead_scene_left_, rect.top(), width, rect.height()));
|
||||
}
|
||||
|
||||
if (snapped_) {
|
||||
painter->setPen(palette().text().color());
|
||||
|
||||
foreach (const rational& r, snap_time_) {
|
||||
double x = TimeToScene(r);
|
||||
|
||||
painter->drawLine(x, rect.top(), x, rect.height());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rational TimelineViewBase::GetPlayheadTime() const
|
||||
@@ -118,6 +150,17 @@ bool TimelineViewBase::PlayheadMove(QMouseEvent *event)
|
||||
|
||||
int64_t target_ts = qMax(static_cast<int64_t>(0), Timecode::time_to_timestamp(mouse_time, timebase()));
|
||||
|
||||
if (Core::instance()->snapping() && snap_service_) {
|
||||
rational target_time = Timecode::timestamp_to_time(target_ts, timebase());
|
||||
rational movement;
|
||||
|
||||
snap_service_->SnapPoint({target_time}, &movement, SnapService::kSnapAll & ~SnapService::kSnapToPlayhead);
|
||||
|
||||
if (!movement.isNull()) {
|
||||
target_ts = Timecode::time_to_timestamp(target_time + movement, timebase());
|
||||
}
|
||||
}
|
||||
|
||||
SetTime(target_ts);
|
||||
emit TimeChanged(target_ts);
|
||||
|
||||
@@ -129,6 +172,10 @@ bool TimelineViewBase::PlayheadRelease(QMouseEvent*)
|
||||
if (dragging_playhead_) {
|
||||
dragging_playhead_ = false;
|
||||
|
||||
if (snap_service_) {
|
||||
snap_service_->HideSnaps();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "core.h"
|
||||
#include "handmovableview.h"
|
||||
#include "timelineplayhead.h"
|
||||
#include "widget/timelinewidget/snapservice.h"
|
||||
#include "widget/timelinewidget/timelinescaledobject.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
@@ -38,6 +39,15 @@ public:
|
||||
|
||||
static const double kMaximumScale;
|
||||
|
||||
void EnableSnap(const QList<rational>& points);
|
||||
void DisableSnap();
|
||||
bool IsSnapped() const
|
||||
{
|
||||
return snapped_;
|
||||
}
|
||||
|
||||
void SetSnapService(SnapService* service);
|
||||
|
||||
public slots:
|
||||
void SetTime(const int64_t time);
|
||||
|
||||
@@ -89,8 +99,13 @@ private:
|
||||
|
||||
bool limit_y_axis_;
|
||||
|
||||
bool snapped_;
|
||||
QList<rational> snap_time_;
|
||||
|
||||
rational end_time_;
|
||||
|
||||
SnapService* snap_service_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Slot called whenever the view resizes or the scene contents change to enforce minimum scene sizes
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <QtMath>
|
||||
|
||||
#include "common/qtutils.h"
|
||||
#include "core.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -32,7 +33,8 @@ SeekableWidget::SeekableWidget(QWidget* parent) :
|
||||
TimelineScaledWidget(parent),
|
||||
time_(0),
|
||||
timeline_points_(nullptr),
|
||||
scroll_(0)
|
||||
scroll_(0),
|
||||
snap_service_(nullptr)
|
||||
{
|
||||
QFontMetrics fm = fontMetrics();
|
||||
|
||||
@@ -59,6 +61,11 @@ void SeekableWidget::ConnectTimelinePoints(TimelinePoints *points)
|
||||
update();
|
||||
}
|
||||
|
||||
void SeekableWidget::SetSnapService(SnapService *service)
|
||||
{
|
||||
snap_service_ = service;
|
||||
}
|
||||
|
||||
const int64_t &SeekableWidget::GetTime() const
|
||||
{
|
||||
return time_;
|
||||
@@ -81,6 +88,15 @@ void SeekableWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
void SeekableWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
|
||||
if (snap_service_) {
|
||||
snap_service_->HideSnaps();
|
||||
}
|
||||
}
|
||||
|
||||
void SeekableWidget::ScaleChangedEvent(const double &)
|
||||
{
|
||||
update();
|
||||
@@ -134,6 +150,20 @@ void SeekableWidget::SeekToScreenPoint(int screen)
|
||||
{
|
||||
int64_t timestamp = qMax(static_cast<int64_t>(0), ScreenToUnitRounded(screen));
|
||||
|
||||
if (Core::instance()->snapping() && snap_service_) {
|
||||
rational playhead_time = Timecode::timestamp_to_time(timestamp, timebase());
|
||||
rational movement;
|
||||
|
||||
snap_service_->SnapPoint({playhead_time},
|
||||
&movement,
|
||||
SnapService::kSnapAll & ~SnapService::kSnapToPlayhead);
|
||||
|
||||
if (!movement.isNull()) {
|
||||
timestamp = Timecode::time_to_timestamp(playhead_time + movement,
|
||||
timebase());
|
||||
}
|
||||
}
|
||||
|
||||
SetTime(timestamp);
|
||||
|
||||
emit TimeChanged(timestamp);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "common/rational.h"
|
||||
#include "timeline/timelinepoints.h"
|
||||
#include "widget/timelinewidget/snapservice.h"
|
||||
#include "widget/timelinewidget/view/timelineplayhead.h"
|
||||
#include "widget/timelinewidget/timelinescaledobject.h"
|
||||
|
||||
@@ -40,6 +41,8 @@ public:
|
||||
|
||||
void ConnectTimelinePoints(TimelinePoints* points);
|
||||
|
||||
void SetSnapService(SnapService* service);
|
||||
|
||||
public slots:
|
||||
void SetTime(const int64_t &r);
|
||||
|
||||
@@ -50,6 +53,7 @@ protected:
|
||||
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
virtual void ScaleChangedEvent(const double&) override;
|
||||
|
||||
@@ -105,6 +109,8 @@ private:
|
||||
|
||||
int playhead_width_;
|
||||
|
||||
SnapService* snap_service_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
Reference in New Issue
Block a user