diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingAxis.cc b/src/UI/DocumentViews/AudioVisualizer/TimingAxis.cc index 248dd011eddb570a8e93cbe6a4c04fea4d093c65..668c618299670cb94bc097c918381ffec4e97598 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingAxis.cc +++ b/src/UI/DocumentViews/AudioVisualizer/TimingAxis.cc @@ -59,6 +59,7 @@ TimingAxis::refreshTicks() minorTicks = 0; majorTicks = availableTicks[minorTicksIndex]; } + TimingUtils::setTicks(minorTicks, majorTicks); } void @@ -83,7 +84,7 @@ TimingAxis::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWi */ for (int i = 0; i < audioLength; i += majorTicks) { int pos = TimingUtils::posFromMs(i, width, audioLength); - painter->drawText(QPoint(pos, yText), msToString(i)); + painter->drawText(QPoint(pos, yText), TimingUtils::printMajorTicks(i)); painter->drawLine(pos, 0, pos, -majorTicksHeight); } if (minorTicks > 0) { @@ -94,16 +95,3 @@ TimingAxis::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWi } } } - -QString -TimingAxis::msToString(int i) const noexcept -{ - QString ret( - QStringLiteral("%1").arg(i % 60000 / 1000, i >= 60000 ? 2 : 1, 10, QLatin1Char('0')) + - QString(".") + QString::number(i % 1000 / majorTicks)); - if (i >= 60000) - ret.prepend(QString::number(i / 60000) + QString(":")); - if (i >= 360000) - ret.prepend(QString::number(i / 360000) + QString(":")); - return ret; -} diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingAxis.hh b/src/UI/DocumentViews/AudioVisualizer/TimingAxis.hh index 1f7196e48a3a4d248bab70bc90fe568957355bb7..243468ac98537f0777c7db5c5c3e83e4dcdb17d7 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingAxis.hh +++ b/src/UI/DocumentViews/AudioVisualizer/TimingAxis.hh @@ -50,8 +50,6 @@ private: int timeDigitsHeight{ 20 }; int timeDigitsMargin{ 3 }; - QString msToString(int i) const noexcept; - protected: public slots: void refreshTicks(); diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingCursor.cc b/src/UI/DocumentViews/AudioVisualizer/TimingCursor.cc new file mode 100644 index 0000000000000000000000000000000000000000..349a1f1e852cb8d2754ac14730763a1994e8a5b9 --- /dev/null +++ b/src/UI/DocumentViews/AudioVisualizer/TimingCursor.cc @@ -0,0 +1,41 @@ +#include "TimingCursor.hh" + +#include <QPainter> +#include <QGraphicsScene> + +#include "TimingUtils.hh" + +using namespace Vivy; + +TimingCursor::TimingCursor(int height_, QGraphicsItem *parent) + : QGraphicsItem(parent) + , height(height_) +{ + textRect = QRectF(0, 10, maxWidth, height - 20); // TODO : remove 10/20 magic numbers +} + +QRectF +TimingCursor::boundingRect() const +{ + return QRectF(-maxWidth, 0, maxWidth * 2, height); +} + +void +TimingCursor::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + painter->drawLine(0, 0, 0, height); + + QRectF textRectangle = textRect; + QPointF sceneLeft = mapFromScene(QPointF(0, 0)); + QPointF sceneRight = mapFromScene(QPointF(scene()->sceneRect().right(), 0)); + TimingUtils::adjustFlip(&textRectangle, sceneLeft.rx(), sceneRight.rx()); + + //painter->drawRect(textRectangle); + painter->drawText(textRectangle, Qt::AlignHCenter | Qt::AlignTop, cursorTime); +} + +void +TimingCursor::setTime(QString str) noexcept +{ + cursorTime = str; +} diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingCursor.hh b/src/UI/DocumentViews/AudioVisualizer/TimingCursor.hh new file mode 100644 index 0000000000000000000000000000000000000000..f9026bed4bbb4fb89c8c1eca5dc26f96dc1464df --- /dev/null +++ b/src/UI/DocumentViews/AudioVisualizer/TimingCursor.hh @@ -0,0 +1,29 @@ +#pragma once + +#include <QGraphicsItem> + +namespace Vivy +{ +class TimingCursor final : public QGraphicsItem { +public: + explicit TimingCursor(int, QGraphicsItem *parent = nullptr); + + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + +private: + int height; + QString cursorTime; + // FIXME + // Forced to define a "max-width" for the text, + // as it doesn't seem possible to get the width of the text. + // We should try drawing it as a QGraphicsTextItem and see if + // boundingRect()->width() is appropriate + qreal maxWidth{ 50 }; // TODO: proper variable + QRectF textRect; + +public: + void setTime(QString str) noexcept; +}; + +} diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingScene.cc b/src/UI/DocumentViews/AudioVisualizer/TimingScene.cc index a99fd4b02fef2cd6938828e5147966249b98f488..383c4aab6fec7776ee9b7b2ae828296fb94221c5 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingScene.cc +++ b/src/UI/DocumentViews/AudioVisualizer/TimingScene.cc @@ -11,6 +11,8 @@ #include <QScrollArea> #include <QScrollBar> #include <QVBoxLayout> +#include <QGraphicsItem> +#include <QGraphicsScene> #include "../../../Lib/Utils.hh" #include "../../../Lib/Ass/Ass.hh" @@ -50,7 +52,7 @@ TimingScene::rebuildScene() } // We put a common origin for the items at the bottom of the axis - int timingAxisHeight = 30; + int timingAxisHeight = 30; // TODO: remove magic number QPixmap pixmap(QPixmap::fromImage(img)); backgroundImg = addPixmap(pixmap); @@ -63,6 +65,10 @@ TimingScene::rebuildScene() // Freeze the scene boundaries setSceneRect(sceneRect()); + cursor = new TimingCursor(pixmap.height()); + addItem(cursor); + cursor->setPos(0, timingAxisHeight); + if (auto assDocument = currentVivyDocument->getAssSubDocument()) { QVector<Ass::LinePtr> lines = assDocument->getLines(); for (int i = 0; i < lines.size(); ++i) { @@ -90,6 +96,16 @@ TimingScene::mousePressEvent(QGraphicsSceneMouseEvent *event) noexcept QGraphicsScene::mousePressEvent(event); } +void +TimingScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) noexcept +{ + int x = int(mouseEvent->scenePos().rx()); + cursor->setPos(x, 30); + cursor->setTime(TimingUtils::printCursor( + TimingUtils::msFromPos(x, int(audioStream->getLength()), width()))); + QGraphicsScene::mouseMoveEvent(mouseEvent); +} + void TimingScene::handleMousePressEventLine(QGraphicsSceneMouseEvent *event, Ass::LinePtr p) noexcept { diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingScene.hh b/src/UI/DocumentViews/AudioVisualizer/TimingScene.hh index 87779e0cf6f3ab9cf8f500284451f5d8fc649411..832923b11650f383b0ab23eea9acef7e22324a65 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingScene.hh +++ b/src/UI/DocumentViews/AudioVisualizer/TimingScene.hh @@ -5,6 +5,7 @@ #include "../../../Lib/Ass/Ass.hh" #include "TimingBar.hh" #include "TimingAxis.hh" +#include "TimingCursor.hh" namespace Vivy { @@ -25,6 +26,7 @@ public: private: QGraphicsPixmapItem *backgroundImg{ nullptr }; QImage img; + TimingCursor *cursor; AudioContext::StreamPtr audioStream; Ass::LineWeakPtr currentLine{}; TimingMode timingMode{ TimingMode::Line }; @@ -33,6 +35,7 @@ private: public: QGraphicsPixmapItem *bg() noexcept; void mousePressEvent(QGraphicsSceneMouseEvent *event) noexcept override; + void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) noexcept override; TimingAxis *getAxis(); diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingUtils.cc b/src/UI/DocumentViews/AudioVisualizer/TimingUtils.cc index d9a398393d60b6b20c107df67cde980e7b5f1500..2e847ba2f1378aad0612fe4b411b60b523ef463e 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingUtils.cc +++ b/src/UI/DocumentViews/AudioVisualizer/TimingUtils.cc @@ -1,3 +1,56 @@ #include "TimingUtils.hh" using namespace Vivy; + +int TimingUtils::majorTicks{ 0 }; +int TimingUtils::minorTicks{ 0 }; + +void +TimingUtils::adjustFlip(QRectF *rect, qreal left, qreal right) noexcept +{ + if (Q_UNLIKELY(right - rect->right() < 0)) { + qreal w = rect->width(); + rect->adjust(-w, 0, -w, 0); + } +} + +void +TimingUtils::adjustTranslate(QRectF *rect, qreal left, qreal right) noexcept +{ + qreal d; + qreal hw = rect->width() / 2; + if (Q_UNLIKELY((d = hw + left) > 0)) { + rect->adjust(d, 0, d, 0); + } + if (Q_UNLIKELY((d = right - hw) < 0)) { + rect->adjust(d, 0, d, 0); + } +} + +QString +TimingUtils::printMajorTicks(int t) noexcept +{ + // TODO : adapt to different ticks (not everytime with "." first) + QString ret( + QStringLiteral("%1").arg(t % 60000 / 1000, t >= 60000 ? 2 : 1, 10, QLatin1Char('0')) + + QString(".") + QString::number(t % 1000 / majorTicks)); + if (t >= 60000) + ret.prepend(QString::number(t / 60000) + QString(":")); + if (t >= 360000) + ret.prepend(QString::number(t / 360000) + QString(":")); + return ret; +} + +QString +TimingUtils::printCursor(int t) noexcept +{ + // TODO : adapt to different ticks (not everytime with "." first) + QString ret( + QStringLiteral("%1").arg(t % 60000 / 1000, t >= 60000 ? 2 : 1, 10, QLatin1Char('0')) + + QString(".") + QString::number(t % 1000 / minorTicks)); + if (t >= 60000) + ret.prepend(QString::number(t / 60000) + QString(":")); + if (t >= 360000) + ret.prepend(QString::number(t / 360000) + QString(":")); + return ret; +} diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingUtils.hh b/src/UI/DocumentViews/AudioVisualizer/TimingUtils.hh index d286004d49db7e983c59b4b8c8c41e5f550bdb2f..2f709ac59a42138a7e9cf6f6d186390f2bd0d9e6 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingUtils.hh +++ b/src/UI/DocumentViews/AudioVisualizer/TimingUtils.hh @@ -4,9 +4,16 @@ #error "This is a C++ header" #endif +#include <QRectF> +#include <QString> + namespace Vivy { class TimingUtils { +private: + static int majorTicks; + static int minorTicks; + public: static inline int posFromMs(int t, int audioWidth, int audioLength) noexcept { @@ -17,5 +24,16 @@ public: { return audioWidth == 0 ? 0 : int(qint64(x) * qint64(audioLength) / audioWidth); } + + static inline void setTicks(int t, int T) noexcept + { + minorTicks = t; + majorTicks = T; + }; + + static void adjustTranslate(QRectF *, qreal, qreal) noexcept; + static void adjustFlip(QRectF *, qreal, qreal) noexcept; + static QString printMajorTicks(int) noexcept; + static QString printCursor(int) noexcept; }; } diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingView.cc b/src/UI/DocumentViews/AudioVisualizer/TimingView.cc index 8c31a37524e9ae029a1caa40ac9d38bc5cbfb82c..49fc449af5ccf223cb11323eeec65d15c6338ec4 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingView.cc +++ b/src/UI/DocumentViews/AudioVisualizer/TimingView.cc @@ -7,6 +7,7 @@ TimingView::TimingView(QGraphicsScene *scene, QImage img, AudioContext::StreamPt : QGraphicsView(scene, parent) , audioStream(stream) { + setMouseTracking(true); int interestingViewHeight = int(scene->height() + horizontalScrollBar()->height()); setFixedHeight(interestingViewHeight); setMaximumHeight(interestingViewHeight);