From 3564e71c1c5d3b1bd511280dec51edc2b416d21b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 24 Jul 2019 21:46:28 -0700 Subject: [PATCH] time ruler interactivity --- app/common/CMakeLists.txt | 2 + app/common/timecodefunctions.cpp | 22 ++++++ app/common/timecodefunctions.h | 8 +++ app/widget/timeruler/timeruler.cpp | 110 +++++++++++++++-------------- app/widget/timeruler/timeruler.h | 20 ++++-- app/widget/viewer/viewer.cpp | 4 ++ app/widget/viewer/viewer.h | 6 ++ 7 files changed, 113 insertions(+), 59 deletions(-) diff --git a/app/common/CMakeLists.txt b/app/common/CMakeLists.txt index 5ac00026b..13ba02e59 100644 --- a/app/common/CMakeLists.txt +++ b/app/common/CMakeLists.txt @@ -24,5 +24,7 @@ set(OLIVE_SOURCES common/qobjectlistcast.h common/qtversionabstraction.h common/qtversionabstraction.cpp + common/timecodefunctions.h + common/timecodefunctions.cpp PARENT_SCOPE ) diff --git a/app/common/timecodefunctions.cpp b/app/common/timecodefunctions.cpp index b94b54fe9..73e50f772 100644 --- a/app/common/timecodefunctions.cpp +++ b/app/common/timecodefunctions.cpp @@ -1,2 +1,24 @@ #include "timecodefunctions.h" +#include + +QString padded(int arg, int padding) { + return QString("%1").arg(arg, padding, 10, QChar('0')); +} + +QString olive::timestamp_to_timecode(const rational ×tamp) +{ + double timestamp_dbl = timestamp.ToDouble(); + + int total_seconds = qFloor(timestamp_dbl); + + int hours = total_seconds / 3600; + int mins = total_seconds / 60 - hours * 60; + int secs = total_seconds - mins * 60; + int fraction = qRound((timestamp_dbl - total_seconds) * 1000); + + return QString("%1:%2:%3.%4").arg(padded(hours, 2), + padded(mins, 2), + padded(secs, 2), + padded(fraction, 3)); +} diff --git a/app/common/timecodefunctions.h b/app/common/timecodefunctions.h index 264c18327..1ed5a7108 100644 --- a/app/common/timecodefunctions.h +++ b/app/common/timecodefunctions.h @@ -1,6 +1,14 @@ #ifndef TIMECODEFUNCTIONS_H #define TIMECODEFUNCTIONS_H +#include +#include "common/rational.h" + +namespace olive { + +QString timestamp_to_timecode(const rational& timestamp); + +} #endif // TIMECODEFUNCTIONS_H diff --git a/app/widget/timeruler/timeruler.cpp b/app/widget/timeruler/timeruler.cpp index 1ee59563e..9a66bfe76 100644 --- a/app/widget/timeruler/timeruler.cpp +++ b/app/widget/timeruler/timeruler.cpp @@ -1,19 +1,24 @@ #include "timeruler.h" #include +#include #include #include +#include "common/timecodefunctions.h" #include "common/qtversionabstraction.h" TimeRuler::TimeRuler(bool text_visible, QWidget* parent) : QWidget(parent), scroll_(0), centered_text_(true), - scale_(16.0) + scale_(16.0), + time_(0) { QFontMetrics fm = fontMetrics(); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + // Text height is used to calculate widget height text_height_ = fm.height(); @@ -24,12 +29,9 @@ TimeRuler::TimeRuler(bool text_visible, QWidget* parent) : // Text visibility affects height, so we set that here SetTextVisible(text_visible); - // FIXME: Test code - time_base_ = rational(1001, 30000); - connect(&test_timer_, SIGNAL(timeout()), this, SLOT(TimeOut())); - test_timer_.setInterval(20); - test_timer_.start(); - // End test code + // FIXME: TEST CODE + SetTimebase(rational(1001, 30000)); + // END TEST CODE } void TimeRuler::SetTextVisible(bool e) @@ -61,6 +63,13 @@ void TimeRuler::SetTimebase(const rational &r) update(); } +void TimeRuler::SetTime(const int64_t &r) +{ + time_ = r; + + update(); +} + void TimeRuler::SetScroll(int s) { scroll_ = s; @@ -79,31 +88,10 @@ void TimeRuler::paintEvent(QPaintEvent *e) QPainter p(this); - int last_unit = -1; + int64_t last_unit = -1; int last_sec = -1; - /* - // Test code that draws a line every single time unit - for (int i=0;i last_unit) { - - if (sec > last_sec) { - p.setPen(Qt::red); - last_sec = sec; - } else { - p.setPen(Qt::white); - } - - p.drawLine(i, height()/2, i, height()); - - last_unit = unit; - } - } - */ - - // Depending on the scale, + // Depending on the scale, we don't need all the lines drawn or else they'll start to become unhelpful // Determine an even number to divide the frame count by int rough_frames_in_second = qRound(time_base_.flipped().ToDouble()); int test_divider = 1; @@ -129,7 +117,7 @@ void TimeRuler::paintEvent(QPaintEvent *e) if (text_visible_) { QFontMetrics fm = p.fontMetrics(); double width_of_second = time_base_.flipped().ToDouble() * scale_; - int average_text_width = QFontMetricsWidth(&fm, "00:00:00;00"); // FIXME: Hardcoded string + int average_text_width = QFontMetricsWidth(&fm, olive::timestamp_to_timecode(0)); half_average_text_width = average_text_width/2; while (width_of_second * text_skip < average_text_width) { text_skip++; @@ -146,6 +134,7 @@ void TimeRuler::paintEvent(QPaintEvent *e) } // Set line color to main text color + p.setBrush(Qt::NoBrush); p.setPen(palette().text().color()); // Calculate where each line starts @@ -157,14 +146,21 @@ void TimeRuler::paintEvent(QPaintEvent *e) int line_halfsec_bottom = line_top + line_length / 3 * 2; int line_frame_bottom = line_top + line_length / 3; + int playhead_pos = -1; + for (int i=loop_start;i qFloor(last_unit/real_divider)) { + if (qFloor(double(unit)/real_divider) > qFloor(double(last_unit)/real_divider)) { // Determine if this unit is a whole second or not - int sec = qFloor(unit * time_base_.ToDouble()); + int sec = qFloor(double(unit) * time_base_.ToDouble()); if (sec > last_sec) { // This line marks a second so we make it long @@ -174,7 +170,7 @@ void TimeRuler::paintEvent(QPaintEvent *e) // Try to draw text here if (text_visible_ && sec%text_skip == 0) { - QString timecode_string = QString("00:00:%1;00").arg(sec, 2, 10, QChar('0')); + QString timecode_string = olive::timestamp_to_timecode(sec); int text_x = i; @@ -203,9 +199,23 @@ void TimeRuler::paintEvent(QPaintEvent *e) } } - p.setPen(Qt::NoPen); - p.setBrush(Qt::red); - DrawPlayhead(&p, 100, height()); + if (playhead_pos >= 0) { + p.setPen(Qt::NoPen); + p.setBrush(Qt::red); + DrawPlayhead(&p, playhead_pos, height()); + } +} + +void TimeRuler::mousePressEvent(QMouseEvent *event) +{ + SeekToScreenPoint(event->pos().x()); +} + +void TimeRuler::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton) { + SeekToScreenPoint(event->pos().x()); + } } void TimeRuler::DrawPlayhead(QPainter *p, int x, int y) @@ -226,25 +236,21 @@ void TimeRuler::DrawPlayhead(QPainter *p, int x, int y) p->drawPolygon(points, 5); } -double TimeRuler::UnitToScreen(const int &u) +double TimeRuler::ScreenToUnitFloat(int screen) { - return u * scale_; + return (screen + scroll_) / scale_; } -int TimeRuler::ScreenToUnit(const int &p) +int64_t TimeRuler::ScreenToUnit(int screen) { - double uf = p / scale_; - - int u = qRound(uf); - - if (qFuzzyCompare(UnitToScreen(u), uf)) { - return u; - } - - return -1; + return qFloor(ScreenToUnitFloat(screen)); } -void TimeRuler::TimeOut() +void TimeRuler::SeekToScreenPoint(int screen) { - SetScroll(scroll_ + 2); + int64_t timestamp = qMax(0, qRound(ScreenToUnitFloat(screen))); + + SetTime(timestamp); + + emit TimeChanged(timestamp); } diff --git a/app/widget/timeruler/timeruler.h b/app/widget/timeruler/timeruler.h index 4dfaa3fbf..4ce1e86f3 100644 --- a/app/widget/timeruler/timeruler.h +++ b/app/widget/timeruler/timeruler.h @@ -20,18 +20,28 @@ public: void SetCenteredText(bool c); + void SetTime(const int64_t &r); + public slots: void SetScroll(int s); protected: virtual void paintEvent(QPaintEvent* e) override; + virtual void mousePressEvent(QMouseEvent *event) override; + virtual void mouseMoveEvent(QMouseEvent *event) override; + +signals: + void TimeChanged(int64_t); + private: void DrawPlayhead(QPainter* p, int x, int y); - double UnitToScreen(const int& u); + double ScreenToUnitFloat(int screen); - int ScreenToUnit(const int& p); + int64_t ScreenToUnit(int screen); + + void SeekToScreenPoint(int screen); int text_height_; @@ -47,12 +57,8 @@ private: rational time_base_; -// FIXME: Test code only - QTimer test_timer_; + int64_t time_; -private slots: - void TimeOut(); -// End test code }; #endif // TIMERULER_H diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 1ac4139c8..a0b9251f9 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -35,6 +35,10 @@ ViewerWidget::ViewerWidget(QWidget *parent) : gl_widget_ = new ViewerGLWidget(this); layout->addWidget(gl_widget_); + // Create time ruler + ruler_ = new TimeRuler(false, this); + layout->addWidget(ruler_); + // Create lower controls controls_ = new PlaybackControls(this); controls_->SetTimecodeEnabled(true); diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 170547937..748b3c466 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -28,6 +28,7 @@ #include "common/rational.h" #include "viewerglwidget.h" #include "widget/playbackcontrols/playbackcontrols.h" +#include "widget/timeruler/timeruler.h" /** * @brief An OpenGL-based viewer widget with playback controls (a PlaybackControls widget). @@ -40,6 +41,8 @@ public: void SetPlaybackControlsEnabled(bool enabled); + void SetTimeRulerEnabled(bool enabled); + public slots: /** * @brief Set the texture to draw and draw it @@ -55,8 +58,11 @@ signals: private: ViewerGLWidget* gl_widget_; + PlaybackControls* controls_; + TimeRuler* ruler_; + private slots: void TemporaryTestFunction(); };