diff --git a/README.md b/README.md index 62fb7a4..bf4f431 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,6 @@ These features are not available, some of them are TBD and others are not planne - Limited system integration: - No [SMTC](https://learn.microsoft.com/en-us/uwp/api/windows.media.systemmediatransportcontrols) support under Windows for now - No [MPRIS](https://www.freedesktop.org/wiki/Specifications/mpris-spec/) support under Linux desktop for now - - No "playback progress on taskbar icon" and "taskbar thumbnail buttons" support whatever on Windows or Linux desktop for now - Limited lyrics (`.lrc`) loading support: - Currently no `.tlrc` (for translated lyrics) or `.rlrc` (for romanized lyrics) support. - Multi-line lyrics and duplicated timestamps are not supported diff --git a/mainwindow.cpp b/mainwindow.cpp index 352333a..b6abe4a 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 Gary Wang +// SPDX-FileCopyrightText: 2025 Gary Wang // // SPDX-License-Identifier: MIT @@ -61,6 +61,9 @@ MainWindow::MainWindow(QWidget *parent) m_mediaPlayer->setLoops(QMediaPlayer::Infinite); ui->playlistView->setModel(m_playlistManager->model()); + ui->chapterlistView->setModel(ui->playbackProgressIndicator->chapterModel()); + ui->chapterlistView->setRootIsDecorated(false); + ui->actionHelp->setShortcut(QKeySequence::HelpContents); addAction(ui->actionHelp); ui->actionOpen->setShortcut(QKeySequence::Open); @@ -368,16 +371,6 @@ void MainWindow::on_playBtn_clicked() } } -QString MainWindow::ms2str(qint64 ms) -{ - QTime duaTime(QTime::fromMSecsSinceStartOfDay(ms)); - if (duaTime.hour() > 0) { - return duaTime.toString("h:mm:ss"); - } else { - return duaTime.toString("m:ss"); - } -} - QList MainWindow::strlst2urllst(QStringList strlst) { QList urlList; @@ -520,12 +513,24 @@ void MainWindow::initConnections() }); connect(m_mediaPlayer, &QMediaPlayer::positionChanged, this, [=](qint64 pos) { - ui->nowTimeLabel->setText(ms2str(pos)); + ui->nowTimeLabel->setText(PlaybackProgressIndicator::formatTime(pos)); if (m_mediaPlayer->duration() != 0) { ui->playbackProgressIndicator->setPosition(pos); m_taskbarManager->setProgressValue(pos); } m_lrcbar->playbackPositionChanged(pos, m_mediaPlayer->duration()); + + static QString lastChapterName; + if (ui->playbackProgressIndicator->chapterModel()->rowCount() > 0) { + QString currentChapterName = ui->playbackProgressIndicator->currentChapterName(); + if (currentChapterName != lastChapterName) { + ui->chapterNameBtn->setText(currentChapterName); + lastChapterName = currentChapterName; + } + } else if (!lastChapterName.isEmpty()) { + ui->chapterNameBtn->setText(""); + lastChapterName.clear(); + } }); connect(m_audioOutput, &QAudioOutput::mutedChanged, this, [=](bool muted) { @@ -539,7 +544,7 @@ void MainWindow::initConnections() connect(m_mediaPlayer, &QMediaPlayer::durationChanged, this, [=](qint64 dua) { ui->playbackProgressIndicator->setDuration(dua); m_taskbarManager->setProgressMaximum(dua); - ui->totalTimeLabel->setText(ms2str(dua)); + ui->totalTimeLabel->setText(PlaybackProgressIndicator::formatTime(dua)); }); connect(m_mediaPlayer, &QMediaPlayer::playbackStateChanged, this, [=](QMediaPlayer::PlaybackState newState) { @@ -687,7 +692,16 @@ void MainWindow::on_setSkinBtn_clicked() void MainWindow::on_playListBtn_clicked() { - setFixedSize(size().height() < 200 ? fullSize : miniSize); + if (size().height() < 200) { + setFixedSize(fullSize); + ui->pluginStackedWidget->setCurrentWidget(ui->playlistViewPage); + } else { + if (ui->pluginStackedWidget->currentWidget() == ui->playlistViewPage) { + setFixedSize(miniSize); + } else { + ui->pluginStackedWidget->setCurrentWidget(ui->playlistViewPage); + } + } } void MainWindow::on_playlistView_activated(const QModelIndex &index) @@ -706,6 +720,33 @@ void MainWindow::on_lrcBtn_clicked() } } +void MainWindow::on_chapterlistView_activated(const QModelIndex &index) +{ + if (!index.isValid()) return; + + QModelIndex timeColumnIndex = index.sibling(index.row(), 0); + QStandardItem* timeItem = ui->playbackProgressIndicator->chapterModel()->itemFromIndex(timeColumnIndex); + if (!timeItem) return; + + qint64 chapterStartTime = timeItem->data(PlaybackProgressIndicator::StartTimeMsRole).toLongLong(); + m_mediaPlayer->setPosition(chapterStartTime); +} + +void MainWindow::on_chapterNameBtn_clicked() +{ + if (size().height() < 200) { + setFixedSize(fullSize); + } + ui->pluginStackedWidget->setCurrentWidget(ui->chaptersViewPage); + if (ui->playbackProgressIndicator->chapterModel()->rowCount() > 0) { + const QModelIndex & curChapterItem = ui->playbackProgressIndicator->currentChapterItem(); + if (curChapterItem.isValid()) { + ui->chapterlistView->setCurrentIndex(curChapterItem); + ui->chapterlistView->scrollTo(curChapterItem, QAbstractItemView::EnsureVisible); + } + } +} + void MainWindow::on_actionOpen_triggered() { loadFile(); diff --git a/mainwindow.h b/mainwindow.h index e60c494..3586714 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 Gary Wang +// SPDX-FileCopyrightText: 2025 Gary Wang // // SPDX-License-Identifier: MIT @@ -80,6 +80,8 @@ private slots: void on_playListBtn_clicked(); void on_playlistView_activated(const QModelIndex &index); void on_lrcBtn_clicked(); + void on_chapterlistView_activated(const QModelIndex &index); + void on_chapterNameBtn_clicked(); void on_actionOpen_triggered(); void on_actionHelp_triggered(); diff --git a/mainwindow.ui b/mainwindow.ui index 12c6926..e212e2a 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -109,6 +109,18 @@ QLabel#coverLabel { QListView { color: white; background: rgba(0, 0, 0, 50); +} + +/****** TreeView ******/ + +QTreeView { + color: white; + background: rgba(0, 0, 0, 50); +} + +QHeaderView { + color: white; + background-color: rgba(200, 200, 200, 50); } @@ -358,6 +370,19 @@ QListView { + + + + + 0 + 0 + + + + + + + @@ -626,7 +651,10 @@ QListView { 0 - + + 1 + + 0 @@ -648,6 +676,29 @@ QListView { + + + + 4 + + + 0 + + + 4 + + + 4 + + + + + QAbstractItemView::NoEditTriggers + + + + + diff --git a/playbackprogressindicator.cpp b/playbackprogressindicator.cpp index 93e1c1f..9183ad2 100644 --- a/playbackprogressindicator.cpp +++ b/playbackprogressindicator.cpp @@ -25,6 +25,37 @@ PlaybackProgressIndicator::PlaybackProgressIndicator(QWidget *parent) : { } +QModelIndex PlaybackProgressIndicator::currentChapterItem() const +{ + int currentChapterIndex = -1; + + for (int i = 0; i < m_chapterModel.rowCount(); i++) { + QStandardItem* timeItem = m_chapterModel.item(i, 0); + qint64 chapterStartTime = timeItem->data(PlaybackProgressIndicator::StartTimeMsRole).toLongLong(); + + if (m_position >= chapterStartTime) { + currentChapterIndex = i; + } else { + break; + } + } + + if (currentChapterIndex >= 0) { + return m_chapterModel.index(currentChapterIndex, 0); + } + + return {}; +} + +QString PlaybackProgressIndicator::currentChapterName() const +{ + const QModelIndex timeIndex(currentChapterItem()); + if (timeIndex.isValid()) { + return m_chapterModel.item(timeIndex.row(), 1)->text(); + } + return {}; +} + void PlaybackProgressIndicator::setPosition(qint64 pos) { m_position = pos; @@ -37,14 +68,34 @@ void PlaybackProgressIndicator::setDuration(qint64 dur) emit durationChanged(m_duration); } +QString PlaybackProgressIndicator::formatTime(qint64 milliseconds) +{ + QTime duaTime(QTime::fromMSecsSinceStartOfDay(milliseconds)); + if (duaTime.hour() > 0) { + return duaTime.toString("h:mm:ss"); + } else { + return duaTime.toString("m:ss"); + } +} + void PlaybackProgressIndicator::setChapters(QList > chapters) { - qDebug() << chapters; m_chapterModel.clear(); + + m_chapterModel.setHorizontalHeaderLabels(QStringList() << tr("Time") << tr("Chapter Name")); + for (const std::pair & chapter : chapters) { + QList row; + + QStandardItem * timeItem = new QStandardItem(formatTime(chapter.first)); + timeItem->setData(chapter.first, StartTimeMsRole); + row.append(timeItem); + QStandardItem * chapterItem = new QStandardItem(chapter.second); chapterItem->setData(chapter.first, StartTimeMsRole); - m_chapterModel.appendRow(chapterItem); + row.append(chapterItem); + + m_chapterModel.appendRow(row); } update(); } diff --git a/playbackprogressindicator.h b/playbackprogressindicator.h index f1de64d..a90fe04 100644 --- a/playbackprogressindicator.h +++ b/playbackprogressindicator.h @@ -24,10 +24,15 @@ public: explicit PlaybackProgressIndicator(QWidget *parent = nullptr); ~PlaybackProgressIndicator() = default; + QStandardItemModel* chapterModel() { return &m_chapterModel; } + QModelIndex currentChapterItem() const; + QString currentChapterName() const; + void setPosition(qint64 pos); void setDuration(qint64 dur); void setChapters(QList> chapters); + static QString formatTime(qint64 milliseconds); static QList> tryLoadChapters(const QString & filePath); static QList> tryLoadSidecarChapterFile(const QString & filePath); static QList> tryLoadChaptersFromMetadata(const QString & filePath);