summary refs log tree commit diff
path: root/resources/qml/ui/media/MediaControls.qml
blob: a48f15eab8709a7e2f0db42c546f803ffcd425dc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later

import "../"
import "../../"
import QtMultimedia 5.15
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import im.nheko 1.0

Rectangle {
    id: control

    property alias desiredVolume: volumeSlider.desiredVolume
    property bool muted: false
    property bool playingVideo: false
    property var mediaState
    property bool mediaLoaded: false
    property var duration
    property var positionValue: 0
    property var position
    property bool shouldShowControls: !playingVideo || playerMouseArea.shouldShowControls || volumeSlider.state == "shown"

    signal playPauseActivated()
    signal loadActivated()

    function showControls() {
        controlHideTimer.restart();
    }

    function durationToString(duration) {
        function maybeZeroPrepend(time) {
            return (time < 10) ? "0" + time.toString() : time.toString();
        }

        var totalSeconds = Math.floor(duration / 1000);
        var seconds = totalSeconds % 60;
        var minutes = (Math.floor(totalSeconds / 60)) % 60;
        var hours = (Math.floor(totalSeconds / (60 * 24))) % 24;
        // Always show minutes and don't prepend zero into the leftmost element
        var ss = maybeZeroPrepend(seconds);
        var mm = (hours > 0) ? maybeZeroPrepend(minutes) : minutes.toString();
        var hh = hours.toString();
        if (hours < 1)
            return mm + ":" + ss;

        return hh + ":" + mm + ":" + ss;
    }

    color: {
        var wc = palette.alternateBase;
        return Qt.rgba(wc.r, wc.g, wc.b, 0.5);
    }
    opacity: control.shouldShowControls ? 1 : 0
    height: controlLayout.implicitHeight

    HoverHandler {
        id: playerMouseArea

        property bool shouldShowControls: hovered || controlHideTimer.running || control.mediaState != MediaPlayer.PlayingState

        onHoveredChanged: showControls()
    }

    ColumnLayout {
        id: controlLayout

        enabled: control.shouldShowControls
        spacing: 0
        anchors.bottom: control.bottom
        anchors.left: control.left
        anchors.right: control.right

        NhekoSlider {
            Layout.fillWidth: true
            Layout.leftMargin: Nheko.paddingSmall
            Layout.rightMargin: Nheko.paddingSmall
            enabled: control.mediaLoaded
            value: control.positionValue
            onMoved: control.position = value
            from: 0
            to: control.duration
            alwaysShowSlider: false
        }

        RowLayout {
            Layout.margins: Nheko.paddingSmall
            spacing: Nheko.paddingSmall
            Layout.fillWidth: true

            // Cache/Play/pause button
            ImageButton {
                id: playbackStateImage

                Layout.alignment: Qt.AlignLeft
                buttonTextColor: palette.text
                Layout.preferredHeight: 24
                Layout.preferredWidth: 24
                image: {
                    if (control.mediaLoaded) {
                        if (control.mediaState == MediaPlayer.PlayingState)
                            return ":/icons/icons/ui/pause-symbol.svg";
                        else
                            return ":/icons/icons/ui/play-sign.svg";
                    } else {
                        return ":/icons/icons/ui/download.svg";
                    }
                }
                onClicked: control.mediaLoaded ? control.playPauseActivated() : control.loadActivated()
            }

            ImageButton {
                id: volumeButton

                Layout.alignment: Qt.AlignLeft
                buttonTextColor: palette.text
                Layout.preferredHeight: 24
                Layout.preferredWidth: 24
                image: {
                    if (control.muted || control.desiredVolume <= 0)
                        return ":/icons/icons/ui/volume-off-indicator.svg";
                    else
                        return ":/icons/icons/ui/volume-up.svg";
                }
                onClicked: control.muted = !control.muted
            }

            NhekoSlider {
                id: volumeSlider

                property real desiredVolume: QtMultimedia.convertVolume(volumeSlider.value, QtMultimedia.LogarithmicVolumeScale, QtMultimedia.LinearVolumeScale)

                state: ""
                Layout.alignment: Qt.AlignLeft
                Layout.preferredWidth: 0
                opacity: 0
                orientation: Qt.Horizontal
                value: 1
                onDesiredVolumeChanged: {
                    control.muted = !(desiredVolume > 0);
                }
                transitions: [
                    Transition {
                        from: ""
                        to: "shown"

                        SequentialAnimation {
                            PauseAnimation {
                                duration: 50
                            }

                            NumberAnimation {
                                duration: 100
                                properties: "opacity"
                                easing.type: Easing.InQuad
                            }

                        }

                        NumberAnimation {
                            properties: "Layout.preferredWidth"
                            duration: 150
                        }

                    },
                    Transition {
                        from: "shown"
                        to: ""

                        SequentialAnimation {
                            PauseAnimation {
                                duration: 100
                            }

                            ParallelAnimation {
                                NumberAnimation {
                                    duration: 100
                                    properties: "opacity"
                                    easing.type: Easing.InQuad
                                }

                                NumberAnimation {
                                    properties: "Layout.preferredWidth"
                                    duration: 150
                                }

                            }

                        }

                    }
                ]

                states: State {
                    name: "shown"
                    when: Settings.mobileMode || volumeButton.hovered || volumeSlider.hovered || volumeSlider.pressed

                    PropertyChanges {
                        target: volumeSlider
                        Layout.preferredWidth: 100
                    }

                    PropertyChanges {
                        target: volumeSlider
                        opacity: 1
                    }

                }

            }

            Label {
                Layout.alignment: Qt.AlignRight
                text: (!control.mediaLoaded ? "-- " : durationToString(control.positionValue)) + " / " + durationToString(control.duration)
                color: palette.text
            }

            Item {
                Layout.fillWidth: true
            }

        }

    }

    // For hiding controls on stationary cursor
    Timer {
        id: controlHideTimer

        interval: 1500 //ms
        repeat: false
    }

    // Fade controls in/out
    Behavior on opacity {
        OpacityAnimator {
            duration: 100
        }

    }

}