time ruler interactivity
This commit is contained in:
@@ -24,5 +24,7 @@ set(OLIVE_SOURCES
|
|||||||
common/qobjectlistcast.h
|
common/qobjectlistcast.h
|
||||||
common/qtversionabstraction.h
|
common/qtversionabstraction.h
|
||||||
common/qtversionabstraction.cpp
|
common/qtversionabstraction.cpp
|
||||||
|
common/timecodefunctions.h
|
||||||
|
common/timecodefunctions.cpp
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,2 +1,24 @@
|
|||||||
#include "timecodefunctions.h"
|
#include "timecodefunctions.h"
|
||||||
|
|
||||||
|
#include <QtMath>
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
#ifndef TIMECODEFUNCTIONS_H
|
#ifndef TIMECODEFUNCTIONS_H
|
||||||
#define TIMECODEFUNCTIONS_H
|
#define TIMECODEFUNCTIONS_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "common/rational.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
QString timestamp_to_timecode(const rational& timestamp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // TIMECODEFUNCTIONS_H
|
#endif // TIMECODEFUNCTIONS_H
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
#include "timeruler.h"
|
#include "timeruler.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QMouseEvent>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
|
|
||||||
|
#include "common/timecodefunctions.h"
|
||||||
#include "common/qtversionabstraction.h"
|
#include "common/qtversionabstraction.h"
|
||||||
|
|
||||||
TimeRuler::TimeRuler(bool text_visible, QWidget* parent) :
|
TimeRuler::TimeRuler(bool text_visible, QWidget* parent) :
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
scroll_(0),
|
scroll_(0),
|
||||||
centered_text_(true),
|
centered_text_(true),
|
||||||
scale_(16.0)
|
scale_(16.0),
|
||||||
|
time_(0)
|
||||||
{
|
{
|
||||||
QFontMetrics fm = fontMetrics();
|
QFontMetrics fm = fontMetrics();
|
||||||
|
|
||||||
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
|
||||||
|
|
||||||
// Text height is used to calculate widget height
|
// Text height is used to calculate widget height
|
||||||
text_height_ = fm.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
|
// Text visibility affects height, so we set that here
|
||||||
SetTextVisible(text_visible);
|
SetTextVisible(text_visible);
|
||||||
|
|
||||||
// FIXME: Test code
|
// FIXME: TEST CODE
|
||||||
time_base_ = rational(1001, 30000);
|
SetTimebase(rational(1001, 30000));
|
||||||
connect(&test_timer_, SIGNAL(timeout()), this, SLOT(TimeOut()));
|
// END TEST CODE
|
||||||
test_timer_.setInterval(20);
|
|
||||||
test_timer_.start();
|
|
||||||
// End test code
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeRuler::SetTextVisible(bool e)
|
void TimeRuler::SetTextVisible(bool e)
|
||||||
@@ -61,6 +63,13 @@ void TimeRuler::SetTimebase(const rational &r)
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TimeRuler::SetTime(const int64_t &r)
|
||||||
|
{
|
||||||
|
time_ = r;
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
void TimeRuler::SetScroll(int s)
|
void TimeRuler::SetScroll(int s)
|
||||||
{
|
{
|
||||||
scroll_ = s;
|
scroll_ = s;
|
||||||
@@ -79,31 +88,10 @@ void TimeRuler::paintEvent(QPaintEvent *e)
|
|||||||
|
|
||||||
QPainter p(this);
|
QPainter p(this);
|
||||||
|
|
||||||
int last_unit = -1;
|
int64_t last_unit = -1;
|
||||||
int last_sec = -1;
|
int last_sec = -1;
|
||||||
/*
|
|
||||||
// Test code that draws a line every single time unit
|
|
||||||
for (int i=0;i<width();i++) {
|
|
||||||
int unit = qFloor((i + scroll_) / scale_);
|
|
||||||
int sec = qFloor(unit * time_base_.ToDouble());
|
|
||||||
|
|
||||||
if (unit > last_unit) {
|
// Depending on the scale, we don't need all the lines drawn or else they'll start to become unhelpful
|
||||||
|
|
||||||
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,
|
|
||||||
// Determine an even number to divide the frame count by
|
// Determine an even number to divide the frame count by
|
||||||
int rough_frames_in_second = qRound(time_base_.flipped().ToDouble());
|
int rough_frames_in_second = qRound(time_base_.flipped().ToDouble());
|
||||||
int test_divider = 1;
|
int test_divider = 1;
|
||||||
@@ -129,7 +117,7 @@ void TimeRuler::paintEvent(QPaintEvent *e)
|
|||||||
if (text_visible_) {
|
if (text_visible_) {
|
||||||
QFontMetrics fm = p.fontMetrics();
|
QFontMetrics fm = p.fontMetrics();
|
||||||
double width_of_second = time_base_.flipped().ToDouble() * scale_;
|
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;
|
half_average_text_width = average_text_width/2;
|
||||||
while (width_of_second * text_skip < average_text_width) {
|
while (width_of_second * text_skip < average_text_width) {
|
||||||
text_skip++;
|
text_skip++;
|
||||||
@@ -146,6 +134,7 @@ void TimeRuler::paintEvent(QPaintEvent *e)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set line color to main text color
|
// Set line color to main text color
|
||||||
|
p.setBrush(Qt::NoBrush);
|
||||||
p.setPen(palette().text().color());
|
p.setPen(palette().text().color());
|
||||||
|
|
||||||
// Calculate where each line starts
|
// 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_halfsec_bottom = line_top + line_length / 3 * 2;
|
||||||
int line_frame_bottom = line_top + line_length / 3;
|
int line_frame_bottom = line_top + line_length / 3;
|
||||||
|
|
||||||
|
int playhead_pos = -1;
|
||||||
|
|
||||||
for (int i=loop_start;i<loop_end;i++) {
|
for (int i=loop_start;i<loop_end;i++) {
|
||||||
int unit = qFloor((i + scroll_) / scale_);
|
int64_t unit = ScreenToUnit(i);
|
||||||
|
|
||||||
|
// Queue a playhead draw
|
||||||
|
if (unit == time_ && playhead_pos == -1) {
|
||||||
|
playhead_pos = i;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if enough space has passed since the last line drawn
|
// Check if enough space has passed since the last line drawn
|
||||||
if (qFloor(unit/real_divider) > 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
|
// 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) {
|
if (sec > last_sec) {
|
||||||
// This line marks a second so we make it long
|
// This line marks a second so we make it long
|
||||||
@@ -174,7 +170,7 @@ void TimeRuler::paintEvent(QPaintEvent *e)
|
|||||||
|
|
||||||
// Try to draw text here
|
// Try to draw text here
|
||||||
if (text_visible_ && sec%text_skip == 0) {
|
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;
|
int text_x = i;
|
||||||
|
|
||||||
@@ -203,9 +199,23 @@ void TimeRuler::paintEvent(QPaintEvent *e)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.setPen(Qt::NoPen);
|
if (playhead_pos >= 0) {
|
||||||
p.setBrush(Qt::red);
|
p.setPen(Qt::NoPen);
|
||||||
DrawPlayhead(&p, 100, height());
|
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)
|
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);
|
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_;
|
return qFloor(ScreenToUnitFloat(screen));
|
||||||
|
|
||||||
int u = qRound(uf);
|
|
||||||
|
|
||||||
if (qFuzzyCompare(UnitToScreen(u), uf)) {
|
|
||||||
return u;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeRuler::TimeOut()
|
void TimeRuler::SeekToScreenPoint(int screen)
|
||||||
{
|
{
|
||||||
SetScroll(scroll_ + 2);
|
int64_t timestamp = qMax(0, qRound(ScreenToUnitFloat(screen)));
|
||||||
|
|
||||||
|
SetTime(timestamp);
|
||||||
|
|
||||||
|
emit TimeChanged(timestamp);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,18 +20,28 @@ public:
|
|||||||
|
|
||||||
void SetCenteredText(bool c);
|
void SetCenteredText(bool c);
|
||||||
|
|
||||||
|
void SetTime(const int64_t &r);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void SetScroll(int s);
|
void SetScroll(int s);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void paintEvent(QPaintEvent* e) override;
|
virtual void paintEvent(QPaintEvent* e) override;
|
||||||
|
|
||||||
|
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void TimeChanged(int64_t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DrawPlayhead(QPainter* p, int x, int y);
|
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_;
|
int text_height_;
|
||||||
|
|
||||||
@@ -47,12 +57,8 @@ private:
|
|||||||
|
|
||||||
rational time_base_;
|
rational time_base_;
|
||||||
|
|
||||||
// FIXME: Test code only
|
int64_t time_;
|
||||||
QTimer test_timer_;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void TimeOut();
|
|
||||||
// End test code
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TIMERULER_H
|
#endif // TIMERULER_H
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
|||||||
gl_widget_ = new ViewerGLWidget(this);
|
gl_widget_ = new ViewerGLWidget(this);
|
||||||
layout->addWidget(gl_widget_);
|
layout->addWidget(gl_widget_);
|
||||||
|
|
||||||
|
// Create time ruler
|
||||||
|
ruler_ = new TimeRuler(false, this);
|
||||||
|
layout->addWidget(ruler_);
|
||||||
|
|
||||||
// Create lower controls
|
// Create lower controls
|
||||||
controls_ = new PlaybackControls(this);
|
controls_ = new PlaybackControls(this);
|
||||||
controls_->SetTimecodeEnabled(true);
|
controls_->SetTimecodeEnabled(true);
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "common/rational.h"
|
#include "common/rational.h"
|
||||||
#include "viewerglwidget.h"
|
#include "viewerglwidget.h"
|
||||||
#include "widget/playbackcontrols/playbackcontrols.h"
|
#include "widget/playbackcontrols/playbackcontrols.h"
|
||||||
|
#include "widget/timeruler/timeruler.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief An OpenGL-based viewer widget with playback controls (a PlaybackControls widget).
|
* @brief An OpenGL-based viewer widget with playback controls (a PlaybackControls widget).
|
||||||
@@ -40,6 +41,8 @@ public:
|
|||||||
|
|
||||||
void SetPlaybackControlsEnabled(bool enabled);
|
void SetPlaybackControlsEnabled(bool enabled);
|
||||||
|
|
||||||
|
void SetTimeRulerEnabled(bool enabled);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/**
|
/**
|
||||||
* @brief Set the texture to draw and draw it
|
* @brief Set the texture to draw and draw it
|
||||||
@@ -55,8 +58,11 @@ signals:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
ViewerGLWidget* gl_widget_;
|
ViewerGLWidget* gl_widget_;
|
||||||
|
|
||||||
PlaybackControls* controls_;
|
PlaybackControls* controls_;
|
||||||
|
|
||||||
|
TimeRuler* ruler_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void TemporaryTestFunction();
|
void TemporaryTestFunction();
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user