diff --git a/app/common/timecodefunctions.cpp b/app/common/timecodefunctions.cpp index 73e50f772..6a549d95d 100644 --- a/app/common/timecodefunctions.cpp +++ b/app/common/timecodefunctions.cpp @@ -6,19 +6,47 @@ QString padded(int arg, int padding) { return QString("%1").arg(arg, padding, 10, QChar('0')); } -QString olive::timestamp_to_timecode(const rational ×tamp) +QString olive::timestamp_to_timecode(const rational& timestamp, + const rational& timebase, + const TimecodeDisplay& display) { 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)); + switch (display) { + case kTimecodeFrames: + case kTimecodeSeconds: + { + int total_seconds = qFloor(timestamp_dbl); + + int hours = total_seconds / 3600; + int mins = total_seconds / 60 - hours * 60; + int secs = total_seconds - mins * 60; + + if (display == kTimecodeSeconds) { + 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)); + } else { + rational frame_rate = timebase.flipped(); + + int frames = qRound((timestamp_dbl - total_seconds) * frame_rate.ToDouble()); + + return QString("%1:%2:%3;%4").arg(padded(hours, 2), + padded(mins, 2), + padded(secs, 2), + padded(frames, 2)); + } + } + case kFrames: + return QString::number(timestamp.numerator() / timebase.numerator()); + case kMilliseconds: + return QString::number(qFloor(timestamp_dbl * 1000)); + } + + return QString(); } diff --git a/app/common/timecodefunctions.h b/app/common/timecodefunctions.h index 1ed5a7108..feb882950 100644 --- a/app/common/timecodefunctions.h +++ b/app/common/timecodefunctions.h @@ -7,7 +7,14 @@ namespace olive { -QString timestamp_to_timecode(const rational& timestamp); +enum TimecodeDisplay { + kTimecodeFrames, + kTimecodeSeconds, + kFrames, + kMilliseconds +}; + +QString timestamp_to_timecode(const rational& timestamp, const rational& timebase, const TimecodeDisplay& display); } diff --git a/app/widget/playbackcontrols/playbackcontrols.cpp b/app/widget/playbackcontrols/playbackcontrols.cpp index b71595004..6c2928448 100644 --- a/app/widget/playbackcontrols/playbackcontrols.cpp +++ b/app/widget/playbackcontrols/playbackcontrols.cpp @@ -23,10 +23,12 @@ #include #include +#include "common/timecodefunctions.h" #include "ui/icons/icons.h" PlaybackControls::PlaybackControls(QWidget *parent) : - QWidget(parent) + QWidget(parent), + time_base_(0) { // Create lower controls QHBoxLayout* lower_control_layout = new QHBoxLayout(this); @@ -45,7 +47,7 @@ PlaybackControls::PlaybackControls(QWidget *parent) : lower_left_layout->setSpacing(0); lower_left_layout->setMargin(0); - cur_tc_lbl_ = new QLabel("00:00:00;00"); + cur_tc_lbl_ = new QLabel(); lower_left_layout->addWidget(cur_tc_lbl_); lower_left_layout->addStretch(); @@ -112,3 +114,15 @@ void PlaybackControls::SetTimecodeEnabled(bool enabled) lower_left_container_->setVisible(enabled); lower_right_container_->setVisible(enabled); } + +void PlaybackControls::SetTimebase(const rational &r) +{ + time_base_ = r; +} + +void PlaybackControls::SetTime(const rational &r) +{ + Q_ASSERT(time_base_.denominator() != 0); + + cur_tc_lbl_->setText(olive::timestamp_to_timecode(r, time_base_, olive::kTimecodeFrames)); +} diff --git a/app/widget/playbackcontrols/playbackcontrols.h b/app/widget/playbackcontrols/playbackcontrols.h index ffb747c9b..f212ee57c 100644 --- a/app/widget/playbackcontrols/playbackcontrols.h +++ b/app/widget/playbackcontrols/playbackcontrols.h @@ -24,6 +24,8 @@ #include #include +#include "common/rational.h" + /** * @brief A playback controls widget providing buttons for navigating media * @@ -40,6 +42,11 @@ public: */ void SetTimecodeEnabled(bool enabled); + void SetTimebase(const rational& r); + +public slots: + void SetTime(const rational& r); + signals: /** * @brief Signal emitted when "Go to Start" is clicked @@ -72,6 +79,8 @@ private: QLabel* cur_tc_lbl_; QLabel* end_tc_lbl_; + + rational time_base_; }; #endif // PLAYBACKCONTROLS_H diff --git a/app/widget/timeruler/timeruler.cpp b/app/widget/timeruler/timeruler.cpp index 9a66bfe76..13b76e282 100644 --- a/app/widget/timeruler/timeruler.cpp +++ b/app/widget/timeruler/timeruler.cpp @@ -26,12 +26,11 @@ TimeRuler::TimeRuler(bool text_visible, QWidget* parent) : // Mediocre but reliable way of scaling UI objects by font/DPI size minimum_gap_between_lines_ = QFontMetricsWidth(&fm, "H"); + // Set width of playhead marker + playhead_width_ = minimum_gap_between_lines_; + // Text visibility affects height, so we set that here SetTextVisible(text_visible); - - // FIXME: TEST CODE - SetTimebase(rational(1001, 30000)); - // END TEST CODE } void TimeRuler::SetTextVisible(bool e) @@ -107,8 +106,10 @@ void TimeRuler::paintEvent(QPaintEvent *e) qreal real_divider = qMax(1.0, time_base_.flipped().ToDouble() / reverse_divider); // Set where the loop ends (affected by text) - int loop_start = -1; - int loop_end = width(); + int loop_start = - playhead_width_; + int loop_end = width() + playhead_width_; + + olive::TimecodeDisplay display_type = olive::kTimecodeSeconds; // Determine where it can draw text int text_skip = 1; @@ -117,7 +118,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, olive::timestamp_to_timecode(0)); + int average_text_width = QFontMetricsWidth(&fm, olive::timestamp_to_timecode(0, time_base_, display_type)); half_average_text_width = average_text_width/2; while (width_of_second * text_skip < average_text_width) { text_skip++; @@ -170,7 +171,7 @@ void TimeRuler::paintEvent(QPaintEvent *e) // Try to draw text here if (text_visible_ && sec%text_skip == 0) { - QString timecode_string = olive::timestamp_to_timecode(sec); + QString timecode_string = olive::timestamp_to_timecode(sec, time_base_, display_type); int text_x = i; @@ -199,6 +200,7 @@ void TimeRuler::paintEvent(QPaintEvent *e) } } + // If we found the playhead along here, draw it last if (playhead_pos >= 0) { p.setPen(Qt::NoPen); p.setBrush(Qt::red); @@ -223,14 +225,14 @@ void TimeRuler::DrawPlayhead(QPainter *p, int x, int y) p->setRenderHint(QPainter::Antialiasing); int half_text_height = text_height_ / 3; - int half_gap = minimum_gap_between_lines_ / 2; + int half_width = playhead_width_ / 2; QPoint points[] = { QPoint(x, y), - QPoint(x - half_gap, y - half_text_height), - QPoint(x - half_gap, y - text_height_), - QPoint(x + half_gap, y - text_height_), - QPoint(x + half_gap, y - half_text_height) + QPoint(x - half_width, y - half_text_height), + QPoint(x - half_width, y - text_height_), + QPoint(x + half_width, y - text_height_), + QPoint(x + half_width, y - half_text_height) }; p->drawPolygon(points, 5); diff --git a/app/widget/timeruler/timeruler.h b/app/widget/timeruler/timeruler.h index 4ce1e86f3..4bc5085ba 100644 --- a/app/widget/timeruler/timeruler.h +++ b/app/widget/timeruler/timeruler.h @@ -47,6 +47,8 @@ private: int minimum_gap_between_lines_; + int playhead_width_; + int scroll_; bool text_visible_; diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index a0b9251f9..956b3b34f 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -20,8 +20,9 @@ #include "viewer.h" -#include #include +#include +#include ViewerWidget::ViewerWidget(QWidget *parent) : QWidget(parent) @@ -38,6 +39,13 @@ ViewerWidget::ViewerWidget(QWidget *parent) : // Create time ruler ruler_ = new TimeRuler(false, this); layout->addWidget(ruler_); + connect(ruler_, SIGNAL(TimeChanged(int64_t)), this, SLOT(RulerTimeChange(int64_t))); + + // Create scrollbar + scrollbar_ = new QScrollBar(Qt::Horizontal, this); + layout->addWidget(scrollbar_); + connect(scrollbar_, SIGNAL(valueChanged(int)), ruler_, SLOT(SetScroll(int))); + scrollbar_->setPageStep(ruler_->width()); // Create lower controls controls_ = new PlaybackControls(this); @@ -46,16 +54,34 @@ ViewerWidget::ViewerWidget(QWidget *parent) : layout->addWidget(controls_); // FIXME: Test code - connect(controls_, SIGNAL(PlayClicked()), this, SLOT(TemporaryTestFunction())); + SetTimebase(rational(1001, 30000)); // End test code } +void ViewerWidget::SetTimebase(const rational &r) +{ + time_base_ = r; + + ruler_->SetTimebase(r); + controls_->SetTimebase(r); +} + void ViewerWidget::SetTexture(GLuint tex) { gl_widget_->SetTexture(tex); } -void ViewerWidget::TemporaryTestFunction() +void ViewerWidget::RulerTimeChange(int64_t i) { - emit TimeChanged(69); + rational time_set = rational(i, 1) * time_base_; + + controls_->SetTime(time_set); + + emit TimeChanged(time_set); +} + +void ViewerWidget::resizeEvent(QResizeEvent *event) +{ + // Set scrollbar page step to the width + scrollbar_->setPageStep(event->size().width()); } diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 748b3c466..204196841 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -21,9 +21,10 @@ #ifndef VIEWER_WIDGET_H #define VIEWER_WIDGET_H -#include -#include #include +#include +#include +#include #include "common/rational.h" #include "viewerglwidget.h" @@ -43,6 +44,8 @@ public: void SetTimeRulerEnabled(bool enabled); + void SetTimebase(const rational& r); + public slots: /** * @brief Set the texture to draw and draw it @@ -56,6 +59,9 @@ public slots: signals: void TimeChanged(const rational&); +protected: + virtual void resizeEvent(QResizeEvent *event) override; + private: ViewerGLWidget* gl_widget_; @@ -63,8 +69,12 @@ private: TimeRuler* ruler_; + QScrollBar* scrollbar_; + + rational time_base_; + private slots: - void TemporaryTestFunction(); + void RulerTimeChange(int64_t i); }; #endif // VIEWER_WIDGET_H