Show chapters in the progress slider

This commit is contained in:
Tobias Fella 2022-09-06 00:49:29 +02:00 committed by Bart De Vries
parent ba9c302352
commit 6b825fc540
6 changed files with 146 additions and 14 deletions

View File

@ -121,6 +121,7 @@ qt_add_qml_module(kasts URI org.kde.kasts
qml/GlobalSearchField.qml
qml/SearchBar.qml
qml/FilterInlineMessage.qml
qml/ChapterSlider.qml
RESOURCES
../icons/media-playback-cloud.svg
../kasts.svg

View File

@ -70,6 +70,13 @@ QVariant ChapterModel::data(const QModelIndex &index, int role) const
return QVariant::fromValue(m_kformat.formatDuration(m_chapters.at(row)->start() * 1000));
case ChapterRole:
return QVariant::fromValue(m_chapters.at(row));
case DurationRole:
if (m_chapters.size() > row + 1) {
return QVariant::fromValue(m_chapters.at(row + 1)->start() - m_chapters.at(row)->start());
} else {
return QVariant::fromValue(m_duration / 1000 - m_chapters.at(row)->start());
}
default:
return QVariant();
}
@ -96,6 +103,7 @@ QHash<int, QByteArray> ChapterModel::roleNames() const
{StartTimeRole, "start"},
{FormattedStartTimeRole, "formattedStart"},
{ChapterRole, "chapter"},
{DurationRole, "duration"},
};
}
@ -215,3 +223,14 @@ Chapter *ChapterModel::currentChapter() const
}
return nullptr;
}
void ChapterModel::setDuration(int duration)
{
m_duration = duration;
Q_EMIT durationChanged();
}
int ChapterModel::duration() const
{
return m_duration;
}

View File

@ -22,6 +22,7 @@ class ChapterModel : public QAbstractListModel
Q_PROPERTY(Entry *entry READ entry WRITE setEntry NOTIFY entryChanged)
Q_PROPERTY(Chapter *currentChapter READ currentChapter NOTIFY currentChapterChanged)
Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged)
public:
enum RoleNames {
@ -31,6 +32,7 @@ public:
StartTimeRole,
FormattedStartTimeRole,
ChapterRole,
DurationRole,
};
Q_ENUM(RoleNames);
@ -46,9 +48,13 @@ public:
Chapter *currentChapter() const;
void setDuration(int duration);
int duration() const;
Q_SIGNALS:
void entryChanged();
void currentChapterChanged();
void durationChanged();
private:
void load();
@ -60,4 +66,5 @@ private:
QVector<Chapter *> m_chapters;
KFormat m_kformat;
int m_currentChapter = 0;
int m_duration;
};

112
src/qml/ChapterSlider.qml Normal file
View File

@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: 2023 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kasts
Control {
id: root
property alias model: chapters.model
property int value: AudioManager.position
property int duration: AudioManager.duration
function setPlaybackPosition(x) {
AudioManager.position = (x - handle.width / 2) / (root.width - handle.width) * duration
}
Kirigami.Theme.colorSet: Kirigami.Theme.Button
Kirigami.Theme.inherit: false
MouseArea {
anchors.fill: parent
onReleased: setPlaybackPosition(mouseX)
}
RowLayout {
id: layout
anchors.fill: parent
anchors.leftMargin: handle.width / 2
anchors.rightMargin: handle.width / 2
spacing: 1
Repeater {
id: chapters
delegate: Rectangle {
// If we're not dragging, use the more precise method using the AudioManager. If we're dragging, this doesn't work because the AudioManager isn't updated while dragging
property bool isCurrent: dragArea.drag.active ? (x - 1.01 <= handle.centerX && handle.centerX < x + width) : (model.start * 1000 <= AudioManager.position && (model.start + model.duration) * 1000 > AudioManager.position)
Layout.preferredWidth: model.duration * 1000 / root.duration * (layout.width - chapters.count + 1)
Layout.preferredHeight: root.height / 2
Layout.alignment: Qt.AlignVCenter
radius: height / 2
z: 1
color: isCurrent ? Kirigami.Theme.alternateBackgroundColor : Kirigami.Theme.backgroundColor
border {
width: 1
color: Kirigami.ColorUtils.linearInterpolation(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.3)
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
z: 0
ToolTip {
text: model.title
visible: parent.containsMouse
}
onReleased: setPlaybackPosition(mouseX + parent.x)
}
}
}
}
Rectangle {
color: Kirigami.Theme.backgroundColor
visible: chapters.count === 0
border {
width: 1
color: Kirigami.ColorUtils.linearInterpolation(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.3)
}
width: parent.width
height: Kirigami.Units.gridUnit / 2
radius: height / 2
anchors.centerIn: parent
}
Rectangle {
id: handle
property int centerX: x + width / 2
Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet: Kirigami.Theme.Button
height: root.height
width: height
radius: width / 2
anchors.verticalCenter: root.verticalCenter
color: Kirigami.Theme.backgroundColor
border.width: 1
border.color: dragArea.pressed || dragArea.containsMouse ? Kirigami.Theme.highlightColor : Kirigami.ColorUtils.linearInterpolation(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.3)
x: dragArea.drag.active ? 0 : root.value / root.duration * 1000 * (root.width - handle.width)
z: 2
MouseArea {
id: dragArea
anchors.fill: parent
hoverEnabled: true
drag {
target: handle
axis: Drag.XAxis
minimumX: 0
maximumX: root.width - handle.width
threshold: 0
}
onReleased: setPlaybackPosition(handle.x + handle.width / 2)
}
}
}

View File

@ -177,17 +177,14 @@ FocusScope {
Layout.alignment: Qt.AlignVCenter
}
Controls.Slider {
ChapterSlider {
id: durationSlider
model: chapterModel
enabled: AudioManager.entry && AudioManager.PlaybackState != AudioManager.StoppedState && AudioManager.canPlay
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
padding: 0
from: 0
to: AudioManager.duration / 1000
value: AudioManager.position / 1000
onMoved: AudioManager.seek(value * 1000)
handle.implicitWidth: implicitHeight // workaround to make slider handle position itself exactly at the location of the click
Layout.preferredHeight: Kirigami.Units.gridUnit + Kirigami.Units.gridUnit % 4
}
Item {
@ -386,6 +383,7 @@ FocusScope {
ChapterModel {
id: chapterModel
entry: AudioManager.entry ? AudioManager.entry : null
duration: AudioManager.duration
}
Kirigami.Dialog {

View File

@ -35,14 +35,8 @@ Kirigami.Page {
Component {
id: slider
Controls.Slider {
enabled: AudioManager.entry
padding: 0
from: 0
to: AudioManager.duration / 1000
value: AudioManager.position / 1000
onMoved: AudioManager.seek(value * 1000)
handle.implicitWidth: implicitHeight // workaround to make slider handle position itself exactly at the location of the click
ChapterSlider {
model: chapterModel
}
}
@ -229,6 +223,7 @@ Kirigami.Page {
model: ChapterModel {
id: chapterModel
entry: AudioManager.entry ? AudioManager.entry : null
duration: AudioManager.duration / 1000
}
clip: true
visible: chapterList.count !== 0