From 9bbfefaea1928737e7ae3098992786a23b460b6f Mon Sep 17 00:00:00 2001 From: Gary Wang Date: Sat, 18 Jan 2025 00:05:18 +0800 Subject: [PATCH] refactor(UI): prepare for chapter support on seekbar --- CMakeLists.txt | 2 + mainwindow.cpp | 15 ++--- mainwindow.h | 1 - mainwindow.ui | 15 +++-- playbackprogressindicator.cpp | 110 ++++++++++++++++++++++++++++++++++ playbackprogressindicator.h | 52 ++++++++++++++++ 6 files changed, 177 insertions(+), 18 deletions(-) create mode 100644 playbackprogressindicator.cpp create mode 100644 playbackprogressindicator.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 76b6869..f99c3cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,7 @@ set (PMUSIC_CPP_FILES lrcbar.cpp lyricsmanager.cpp fftspectrum.cpp + playbackprogressindicator.cpp ) set (PMUSIC_HEADER_FILES @@ -55,6 +56,7 @@ set (PMUSIC_HEADER_FILES lrcbar.h lyricsmanager.h fftspectrum.h + playbackprogressindicator.h ) set (PMUSIC_UI_FILES diff --git a/mainwindow.cpp b/mainwindow.cpp index 09808ac..d501044 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -359,14 +359,6 @@ void MainWindow::on_stopBtn_clicked() m_mediaPlayer->stop(); } -void MainWindow::on_playbackSlider_valueChanged(int value) -{ - qint64 currPos = m_mediaPlayer->duration() == 0 ? value : m_mediaPlayer->position() * ui->playbackSlider->maximum() / m_mediaPlayer->duration(); - if (qAbs(currPos - value) > 2) { - m_mediaPlayer->setPosition(ui->playbackSlider->value() * 1.0 / ui->playbackSlider->maximum() * m_mediaPlayer->duration()); - } -} - void MainWindow::on_prevBtn_clicked() { QModelIndex index(m_playlistManager->previousIndex()); @@ -414,6 +406,10 @@ void MainWindow::initConnections() m_audioOutput->setDevice(m_mediaDevices->defaultAudioOutput()); }); + connect(ui->playbackProgressIndicator, &PlaybackProgressIndicator::seekingRequested, this, [=](qint64 pos){ + m_mediaPlayer->setPosition(pos); + }); + connect(m_mediaPlayer, &QMediaPlayer::sourceChanged, this, [=](){ QUrl fileUrl(m_mediaPlayer->source()); @@ -470,7 +466,7 @@ void MainWindow::initConnections() connect(m_mediaPlayer, &QMediaPlayer::positionChanged, this, [=](qint64 pos) { ui->nowTimeLabel->setText(ms2str(pos)); if (m_mediaPlayer->duration() != 0) { - ui->playbackSlider->setSliderPosition(ui->playbackSlider->maximum() * pos / m_mediaPlayer->duration()); + ui->playbackProgressIndicator->setPosition(pos); } m_lrcbar->playbackPositionChanged(pos, m_mediaPlayer->duration()); }); @@ -484,6 +480,7 @@ void MainWindow::initConnections() }); connect(m_mediaPlayer, &QMediaPlayer::durationChanged, this, [=](qint64 dua) { + ui->playbackProgressIndicator->setDuration(dua); ui->totalTimeLabel->setText(ms2str(dua)); }); diff --git a/mainwindow.h b/mainwindow.h index 3906fbf..7cdc2d4 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -68,7 +68,6 @@ private slots: void on_playBtn_clicked(); void on_volumeSlider_valueChanged(int value); void on_stopBtn_clicked(); - void on_playbackSlider_valueChanged(int value); void on_prevBtn_clicked(); void on_nextBtn_clicked(); void on_volumeBtn_clicked(); diff --git a/mainwindow.ui b/mainwindow.ui index e869eaf..db2032d 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -371,14 +371,7 @@ QListView { - - - 1000 - - - Qt::Orientation::Horizontal - - + @@ -694,6 +687,12 @@ QListView { QSlider
seekableslider.h
+ + PlaybackProgressIndicator + QWidget +
playbackprogressindicator.h
+ 1 +
diff --git a/playbackprogressindicator.cpp b/playbackprogressindicator.cpp new file mode 100644 index 0000000..ca781a7 --- /dev/null +++ b/playbackprogressindicator.cpp @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: 2025 Gary Wang +// +// SPDX-License-Identifier: MIT + +#include "playbackprogressindicator.h" + +#include +#include + +PlaybackProgressIndicator::PlaybackProgressIndicator(QWidget *parent) : + QWidget(parent) +{ +} + +void PlaybackProgressIndicator::setPosition(qint64 pos) +{ + m_position = pos; + emit positionChanged(m_position); +} + +void PlaybackProgressIndicator::setDuration(qint64 dur) +{ + m_duration = dur; + emit durationChanged(m_duration); +} + +void PlaybackProgressIndicator::setChapters(QList > chapters) +{ + m_chapterModel.clear(); + for (const std::pair & chapter : chapters) { + QStandardItem * chapterItem = new QStandardItem(chapter.second); + chapterItem->setData(chapter.first, StartTimeMsRole); + m_chapterModel.appendRow(chapterItem); + } + update(); +} + +void PlaybackProgressIndicator::paintEvent(QPaintEvent *event) +{ + constexpr int progressBarHeight = 6; + constexpr QColor activeColor = QColor(85, 170, 0); + const QPointF topLeft(0, height() / 2.0 - progressBarHeight / 2.0); + const QSizeF barSize(width(), progressBarHeight); + + const float currentProgress = m_duration <= 0 ? 0 : (m_seekingPosition >= 0 ? m_seekingPosition : m_position) / (float)m_duration; + const QSizeF progressSize(width() * currentProgress, progressBarHeight); + + QPainterPath theProgress; + theProgress.addRoundedRect(QRectF(topLeft, progressSize), progressBarHeight / 2, progressBarHeight / 2); + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.save(); + + // the bar itself + painter.setPen(Qt::gray); + painter.drawRoundedRect(QRectF(topLeft, barSize), progressBarHeight / 2, progressBarHeight / 2); + painter.fillPath(theProgress, activeColor); + + // progress + painter.setPen(activeColor); + painter.drawPath(theProgress); + + // chapter markers + if (m_duration > 0) { + painter.setPen(Qt::lightGray); + for (int i = 0; i < m_chapterModel.rowCount(); i++) { + qint64 chapterStartTime = m_chapterModel.item(i)->data(StartTimeMsRole).toInt(); + if (chapterStartTime > m_duration) break; + float chapterPercent = chapterStartTime / (float)m_duration; + float chapterPosX = width() * chapterPercent; + painter.drawLine(topLeft + QPoint(chapterPosX, 0), + topLeft + QPoint(chapterPosX, progressBarHeight)); + } + } + + painter.restore(); +} + +void PlaybackProgressIndicator::mousePressEvent(QMouseEvent *event) +{ + if (m_duration > 0) { + event->accept(); + } else { + return QWidget::mousePressEvent(event); + } +} + +void PlaybackProgressIndicator::mouseMoveEvent(QMouseEvent *event) +{ + if (m_duration > 0) { + m_seekingPosition = event->position().x() * m_duration / width(); + if (m_seekOnMove) { + emit seekingRequested(m_seekingPosition); + } + update(); + } + return QWidget::mouseMoveEvent(event); +} + +void PlaybackProgressIndicator::mouseReleaseEvent(QMouseEvent *event) +{ + if (m_duration > 0) { + int seekingPosition = event->position().x() * m_duration / width(); + m_seekingPosition = -1; + emit seekingRequested(seekingPosition); + } + update(); + return QWidget::mouseReleaseEvent(event); +} diff --git a/playbackprogressindicator.h b/playbackprogressindicator.h new file mode 100644 index 0000000..0959738 --- /dev/null +++ b/playbackprogressindicator.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2025 Gary Wang +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +class PlaybackProgressIndicator : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(bool seekOnMove MEMBER m_seekOnMove NOTIFY seekOnMoveChanged) + Q_PROPERTY(qint64 position MEMBER m_position NOTIFY positionChanged) + Q_PROPERTY(qint64 duration MEMBER m_duration NOTIFY durationChanged) +public: + enum Roles { + ChapterTitleRole = Qt::DisplayRole, + StartTimeMsRole = Qt::UserRole + 1, + }; + + explicit PlaybackProgressIndicator(QWidget *parent = nullptr); + ~PlaybackProgressIndicator() = default; + + void setPosition(qint64 pos); + void setDuration(qint64 dur); + void setChapters(QList> chapters); + +signals: + void seekOnMoveChanged(bool sow); + void positionChanged(qint64 newPosition); + void durationChanged(qint64 newDuration); + void seekingRequested(qint64 position); + +public slots: + +protected: + void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + +private: + bool m_seekOnMove = true; + qint64 m_position = -1; + qint64 m_seekingPosition = -1; + qint64 m_duration = -1; + QStandardItemModel m_chapterModel; +}; +