1// SPDX-FileCopyrightText: 2021 Nheko Contributors
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5import "../"
6import "../../"
7import QtMultimedia 5.15
8import QtQuick 2.15
9import QtQuick.Controls 2.15
10import QtQuick.Layouts 1.15
11import im.nheko 1.0
12
13Rectangle {
14    id: control
15
16    property alias desiredVolume: volumeSlider.desiredVolume
17    property bool muted: false
18    property bool playingVideo: false
19    property var mediaState
20    property bool mediaLoaded: false
21    property var duration
22    property var positionValue: 0
23    property var position
24    property bool shouldShowControls: !playingVideo || playerMouseArea.shouldShowControls || volumeSlider.state == "shown"
25
26    signal playPauseActivated()
27    signal loadActivated()
28
29    function showControls() {
30        controlHideTimer.restart();
31    }
32
33    function durationToString(duration) {
34        function maybeZeroPrepend(time) {
35            return (time < 10) ? "0" + time.toString() : time.toString();
36        }
37
38        var totalSeconds = Math.floor(duration / 1000);
39        var seconds = totalSeconds % 60;
40        var minutes = (Math.floor(totalSeconds / 60)) % 60;
41        var hours = (Math.floor(totalSeconds / (60 * 24))) % 24;
42        // Always show minutes and don't prepend zero into the leftmost element
43        var ss = maybeZeroPrepend(seconds);
44        var mm = (hours > 0) ? maybeZeroPrepend(minutes) : minutes.toString();
45        var hh = hours.toString();
46        if (hours < 1)
47            return mm + ":" + ss;
48
49        return hh + ":" + mm + ":" + ss;
50    }
51
52    color: {
53        var wc = Nheko.colors.alternateBase;
54        return Qt.rgba(wc.r, wc.g, wc.b, 0.5);
55    }
56    opacity: control.shouldShowControls ? 1 : 0
57    height: controlLayout.implicitHeight
58
59    HoverHandler {
60        id: playerMouseArea
61
62        property bool shouldShowControls: hovered || controlHideTimer.running || control.mediaState != MediaPlayer.PlayingState
63
64        onHoveredChanged: showControls()
65    }
66
67    ColumnLayout {
68        id: controlLayout
69
70        enabled: control.shouldShowControls
71        spacing: 0
72        anchors.bottom: control.bottom
73        anchors.left: control.left
74        anchors.right: control.right
75
76        NhekoSlider {
77            Layout.fillWidth: true
78            Layout.leftMargin: Nheko.paddingSmall
79            Layout.rightMargin: Nheko.paddingSmall
80            enabled: control.mediaLoaded
81            value: control.positionValue
82            onMoved: control.position = value
83            from: 0
84            to: control.duration
85            alwaysShowSlider: false
86        }
87
88        RowLayout {
89            Layout.margins: Nheko.paddingSmall
90            spacing: Nheko.paddingSmall
91            Layout.fillWidth: true
92
93            // Cache/Play/pause button
94            ImageButton {
95                id: playbackStateImage
96
97                Layout.alignment: Qt.AlignLeft
98                buttonTextColor: Nheko.colors.text
99                Layout.preferredHeight: 24
100                Layout.preferredWidth: 24
101                image: {
102                    if (control.mediaLoaded) {
103                        if (control.mediaState == MediaPlayer.PlayingState)
104                            return ":/icons/icons/ui/pause-symbol.svg";
105                        else
106                            return ":/icons/icons/ui/play-sign.svg";
107                    } else {
108                        return ":/icons/icons/ui/download.svg";
109                    }
110                }
111                onClicked: control.mediaLoaded ? control.playPauseActivated() : control.loadActivated()
112            }
113
114            ImageButton {
115                id: volumeButton
116
117                Layout.alignment: Qt.AlignLeft
118                buttonTextColor: Nheko.colors.text
119                Layout.preferredHeight: 24
120                Layout.preferredWidth: 24
121                image: {
122                    if (control.muted || control.desiredVolume <= 0)
123                        return ":/icons/icons/ui/volume-off-indicator.svg";
124                    else
125                        return ":/icons/icons/ui/volume-up.svg";
126                }
127                onClicked: control.muted = !control.muted
128            }
129
130            NhekoSlider {
131                id: volumeSlider
132
133                property real desiredVolume: QtMultimedia.convertVolume(volumeSlider.value, QtMultimedia.LogarithmicVolumeScale, QtMultimedia.LinearVolumeScale)
134
135                state: ""
136                Layout.alignment: Qt.AlignLeft
137                Layout.preferredWidth: 0
138                opacity: 0
139                orientation: Qt.Horizontal
140                value: 1
141                onDesiredVolumeChanged: {
142                    control.muted = !(desiredVolume > 0);
143                }
144                transitions: [
145                    Transition {
146                        from: ""
147                        to: "shown"
148
149                        SequentialAnimation {
150                            PauseAnimation {
151                                duration: 50
152                            }
153
154                            NumberAnimation {
155                                duration: 100
156                                properties: "opacity"
157                                easing.type: Easing.InQuad
158                            }
159
160                        }
161
162                        NumberAnimation {
163                            properties: "Layout.preferredWidth"
164                            duration: 150
165                        }
166
167                    },
168                    Transition {
169                        from: "shown"
170                        to: ""
171
172                        SequentialAnimation {
173                            PauseAnimation {
174                                duration: 100
175                            }
176
177                            ParallelAnimation {
178                                NumberAnimation {
179                                    duration: 100
180                                    properties: "opacity"
181                                    easing.type: Easing.InQuad
182                                }
183
184                                NumberAnimation {
185                                    properties: "Layout.preferredWidth"
186                                    duration: 150
187                                }
188
189                            }
190
191                        }
192
193                    }
194                ]
195
196                states: State {
197                    name: "shown"
198                    when: Settings.mobileMode || volumeButton.hovered || volumeSlider.hovered || volumeSlider.pressed
199
200                    PropertyChanges {
201                        target: volumeSlider
202                        Layout.preferredWidth: 100
203                    }
204
205                    PropertyChanges {
206                        target: volumeSlider
207                        opacity: 1
208                    }
209
210                }
211
212            }
213
214            Label {
215                Layout.alignment: Qt.AlignRight
216                text: (!control.mediaLoaded) ? "-- / --" : (durationToString(control.positionValue) + " / " + durationToString(control.duration))
217                color: Nheko.colors.text
218            }
219
220            Item {
221                Layout.fillWidth: true
222            }
223
224        }
225
226    }
227
228    // For hiding controls on stationary cursor
229    Timer {
230        id: controlHideTimer
231
232        interval: 1500 //ms
233        repeat: false
234    }
235
236    // Fade controls in/out
237    Behavior on opacity {
238        OpacityAnimator {
239            duration: 100
240        }
241
242    }
243
244}
245