|
|
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
|
|
import QtMultimedia 5.15
|
|
|
|
import QtQuick 2.15
|
|
|
|
import QtQuick.Controls 2.15
|
|
|
|
import QtQuick.Layouts 1.2
|
|
|
|
import im.nheko 1.0
|
|
|
|
|
|
|
|
Item {
|
|
|
|
id: control
|
|
|
|
|
|
|
|
property alias desiredVolume: volumeSlider.desiredVolume
|
|
|
|
property alias muted: volumeSlider.muted
|
|
|
|
property alias volumeOrientation: volumeSlider.orientation
|
|
|
|
property var mediaState
|
|
|
|
property bool mediaLoaded: false
|
|
|
|
property var duration
|
|
|
|
property var positionValue: 0
|
|
|
|
property var position
|
|
|
|
property int controlHeight: 25
|
|
|
|
property bool shouldShowControls: playerMouseArea.shouldShowControls || volumeSlider.controlsVisible
|
|
|
|
|
|
|
|
signal playPauseActivated(real mouseX, real mouseY)
|
|
|
|
signal loadActivated(real mouseX, real mouseY)
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
id: playerMouseArea
|
|
|
|
|
|
|
|
property bool shouldShowControls: (containsMouse && controlHideTimer.running) || (control.mediaState != MediaPlayer.PlayingState) || controlRect.contains(mapToItem(controlRect, mouseX, mouseY))
|
|
|
|
|
|
|
|
onClicked: {
|
|
|
|
control.mediaLoaded ? control.playPauseActivated(mouseX, mouseY) : control.loadActivated(mouseX, mouseY);
|
|
|
|
}
|
|
|
|
hoverEnabled: true
|
|
|
|
onPositionChanged: controlHideTimer.start()
|
|
|
|
onExited: controlHideTimer.start()
|
|
|
|
onEntered: controlHideTimer.start()
|
|
|
|
anchors.fill: control
|
|
|
|
propagateComposedEvents: true
|
|
|
|
}
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
id: controlRect
|
|
|
|
|
|
|
|
// Window color with 128/255 alpha
|
|
|
|
color: {
|
|
|
|
var wc = Nheko.colors.alternateBase;
|
|
|
|
return Qt.rgba(wc.r, wc.g, wc.b, 0.5);
|
|
|
|
}
|
|
|
|
anchors.bottom: control.bottom
|
|
|
|
anchors.left: control.left
|
|
|
|
anchors.right: control.right
|
|
|
|
height: 40
|
|
|
|
opacity: control.shouldShowControls ? 1 : 0
|
|
|
|
|
|
|
|
RowLayout {
|
|
|
|
anchors.fill: parent
|
|
|
|
width: parent.width
|
|
|
|
|
|
|
|
// Cache/Play/pause button
|
|
|
|
Image {
|
|
|
|
id: playbackStateImage
|
|
|
|
|
|
|
|
property color controlColor: (playbackStateArea.containsMouse) ? Nheko.colors.highlight : Nheko.colors.text
|
|
|
|
|
|
|
|
fillMode: Image.PreserveAspectFit
|
|
|
|
Layout.preferredHeight: control.controlHeight
|
|
|
|
Layout.alignment: Qt.AlignVCenter
|
|
|
|
source: {
|
|
|
|
if (control.mediaLoaded) {
|
|
|
|
if (control.mediaState == MediaPlayer.PlayingState)
|
|
|
|
return "image://colorimage/:/icons/icons/ui/pause-symbol.png?" + controlColor;
|
|
|
|
else
|
|
|
|
return "image://colorimage/:/icons/icons/ui/play-sign.png?" + controlColor;
|
|
|
|
} else {
|
|
|
|
return "image://colorimage/:/icons/icons/ui/arrow-pointing-down.png?" + controlColor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
id: playbackStateArea
|
|
|
|
|
|
|
|
anchors.fill: parent
|
|
|
|
hoverEnabled: true
|
|
|
|
onClicked: {
|
|
|
|
control.mediaLoaded ? control.playPauseActivated(mouseX, mouseY) : control.loadActivated(mouseX, mouseY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Label {
|
|
|
|
text: (!control.mediaLoaded) ? "-/-" : (durationToString(control.positionValue) + "/" + durationToString(control.duration))
|
|
|
|
color: Nheko.colors.text
|
|
|
|
}
|
|
|
|
|
|
|
|
Slider {
|
|
|
|
Layout.fillWidth: true
|
|
|
|
Layout.minimumWidth: 50
|
|
|
|
height: control.controlHeight
|
|
|
|
value: control.positionValue
|
|
|
|
onMoved: control.position = value
|
|
|
|
from: 0
|
|
|
|
to: control.duration
|
|
|
|
}
|
|
|
|
|
|
|
|
VolumeControl {
|
|
|
|
id: volumeSlider
|
|
|
|
|
|
|
|
Layout.rightMargin: 5
|
|
|
|
Layout.preferredHeight: control.controlHeight
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fade controls in/out
|
|
|
|
Behavior on opacity {
|
|
|
|
OpacityAnimator {
|
|
|
|
duration: 100
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// For hiding controls on stationary cursor
|
|
|
|
Timer {
|
|
|
|
id: controlHideTimer
|
|
|
|
|
|
|
|
interval: 1500 //ms
|
|
|
|
repeat: false
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|