From ffc60180ded0c84387e3a3dbd81ab99ac8079edc Mon Sep 17 00:00:00 2001
From: Nicolas Werner <nicolas.werner@hotmail.de>
Date: Thu, 11 Nov 2021 19:18:45 +0100
Subject: [PATCH] Cleanup positioning of player elements

---
 .../qml/delegates/PlayableMediaMessage.qml    |  42 ++--
 resources/qml/ui/NhekoSlider.qml              |  79 +++----
 resources/qml/ui/media/MediaControls.qml      | 196 ++++++++++++------
 resources/qml/ui/media/VolumeControl.qml      | 117 -----------
 resources/res.qrc                             |   1 -
 5 files changed, 174 insertions(+), 261 deletions(-)
 delete mode 100644 resources/qml/ui/media/VolumeControl.qml

diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml
index 2b4c4d49e..67214dd70 100644
--- a/resources/qml/delegates/PlayableMediaMessage.qml
+++ b/resources/qml/delegates/PlayableMediaMessage.qml
@@ -10,7 +10,7 @@ import QtQuick.Controls 2.15
 import QtQuick.Layouts 1.15
 import im.nheko 1.0
 
-ColumnLayout {
+Item {
     id: content
 
     required property double proportionalHeight
@@ -22,7 +22,13 @@ ColumnLayout {
     required property string body
     required property string filesize
 
-    Layout.fillWidth: true
+        property double tempWidth: Math.min(parent ? parent.width : undefined, originalWidth < 1 ? 400 : originalWidth)
+        property double tempHeight: tempWidth * proportionalHeight
+        property double divisor: isReply ? 4 : 2
+        property bool tooHigh: tempHeight > timelineRoot.height / divisor
+
+        height: (type == MtxEvent.VideoMessage ? tooHigh ? timelineRoot.height / divisor : tempHeight : 80) + fileInfoLabel.height
+        width: type == MtxEvent.VideoMessage ? tooHigh ? (timelineRoot.height / divisor) / proportionalHeight : tempWidth : 250
 
     MxcMedia {
         id: mxcmedia
@@ -38,15 +44,10 @@ ColumnLayout {
 
     Rectangle {
         id: videoContainer
-
-        property double tempWidth: Math.min(parent ? parent.width : undefined, originalWidth < 1 ? 400 : originalWidth)
-        property double tempHeight: tempWidth * proportionalHeight
-        property double divisor: isReply ? 4 : 2
-        property bool tooHigh: tempHeight > timelineRoot.height / divisor
-
         color: type == MtxEvent.VideoMessage ? Nheko.colors.window : "transparent"
-        Layout.preferredHeight: type == MtxEvent.VideoMessage ? tooHigh ? timelineRoot.height / divisor : tempHeight : 80
-        Layout.preferredWidth: type == MtxEvent.VideoMessage ? tooHigh ? (timelineRoot.height / divisor) / proportionalHeight : tempWidth : 250
+        width: parent.width
+        height: parent.height - fileInfoLabel.height
+
 
         Image {
             anchors.fill: parent
@@ -65,14 +66,18 @@ ColumnLayout {
                 flushMode: VideoOutput.FirstFrame
             }
 
+
+        }
+
+    }
+
             MediaControls {
                 id: mediaControls
 
-                anchors.fill: parent
-                x: type == MtxEvent.VideoMessage ? videoOutput.contentRect.x : videoContainer.x
-                y: type == MtxEvent.VideoMessage ? videoOutput.contentRect.y : videoContainer.y
-                width: type == MtxEvent.VideoMessage ? videoOutput.contentRect.width : videoContainer.width
-                height: type == MtxEvent.VideoMessage ? videoOutput.contentRect.height : videoContainer.height
+                anchors.left: content.left
+                anchors.right: content.right
+                anchors.bottom: fileInfoLabel.top
+
                 playingVideo: type == MtxEvent.VideoMessage
                 positionValue: mxcmedia.position
                 duration: mxcmedia.duration
@@ -83,15 +88,12 @@ ColumnLayout {
                 onLoadActivated: mxcmedia.eventId = eventId
             }
 
-        }
-
-    }
-
     // information about file name and file size
     Label {
         id: fileInfoLabel
 
-        Layout.fillWidth: true
+        anchors.bottom: content.bottom
+
         text: body + " [" + filesize + "]"
         textFormat: Text.PlainText
         elide: Text.ElideRight
diff --git a/resources/qml/ui/NhekoSlider.qml b/resources/qml/ui/NhekoSlider.qml
index 887cb80c1..6cf1fd2d7 100644
--- a/resources/qml/ui/NhekoSlider.qml
+++ b/resources/qml/ui/NhekoSlider.qml
@@ -7,71 +7,42 @@ import QtQuick.Controls 2.15
 import im.nheko 1.0
 
 Slider {
-    id: slider
+    id: control
+		value: 0
 
-    property real sliderWidth
-    property real sliderHeight
+		property color progressColor: Nheko.colors.highlight
     property bool alwaysShowSlider: true
+    property int sliderRadius: 16
+    implicitHeight: sliderRadius
 
-    anchors.bottomMargin: orientation == Qt.Vertical ? Nheko.paddingMedium : undefined
-    anchors.topMargin: orientation == Qt.Vertical ? Nheko.paddingMedium : undefined
-    anchors.leftMargin: orientation == Qt.Vertical ? undefined : Nheko.paddingMedium
-    anchors.rightMargin: orientation == Qt.Vertical ? undefined : Nheko.paddingMedium
+		padding: 0
 
     background: Rectangle {
-        x: slider.leftPadding + (slider.orientation == Qt.Vertical ? slider.availableWidth / 2 - width / 2 : 0)
-        y: slider.topPadding + (slider.orientation == Qt.Vertical ? 0 : slider.availableHeight / 2 - height / 2)
-        // implicitWidth: slider.orientation == Qt.Vertical ? 8 : 100
-        // implicitHeight: slider.orientation == Qt.Vertical ? 100 : 8
-        width: slider.orientation == Qt.Vertical ? sliderWidth : slider.availableWidth
-        height: slider.orientation == Qt.Vertical ? slider.availableHeight : sliderHeight
-        radius: 2
-        color: {
-            if (slider.orientation == Qt.Vertical) {
-                return Nheko.colors.highlight;
-            } else {
-                var col = Nheko.colors.buttonText;
-                return Qt.rgba(col.r, col.g, col.b, 0.5);
-            }
-        }
-        border.color: {
-            var col = Nheko.colors.base;
-            return Qt.rgba(col.r, col.g, col.b, 0.5);
-        }
+        x: control.leftPadding + handle.width/2
+        y: control.topPadding + control.availableHeight / 2 - height / 2
+        implicitWidth: 200
+        implicitHeight: control.sliderRadius/4
+        width: control.availableWidth - handle.width
+        height: implicitHeight
+        radius: height/2
+        color: Nheko.colors.buttonText
 
         Rectangle {
-            width: slider.orientation == Qt.Vertical ? parent.width : slider.visualPosition * parent.width
-            height: slider.orientation == Qt.Vertical ? slider.visualPosition * parent.height : parent.height
-            color: {
-                if (slider.orientation == Qt.Vertical) {
-                    return Nheko.colors.buttonText;
-                } else {
-                    return Nheko.colors.highlight;
-                }
-            }
+            width: control.visualPosition * parent.width
+            height: parent.height
+            color: control.progressColor
             radius: 2
         }
-
     }
 
     handle: Rectangle {
-        x: {
-            if (slider.orientation == Qt.Vertical)
-                return slider.leftPadding + slider.availableWidth / 2 - width / 2;
-            else
-                return slider.leftPadding + slider.visualPosition * (slider.availableWidth - width);
-        }
-        y: {
-            if (slider.orientation == Qt.Vertical)
-                return slider.topPadding + slider.visualPosition * (slider.availableHeight - height);
-            else
-                return slider.topPadding + slider.availableHeight / 2 - height / 2;
-        }
-        implicitWidth: 16
-        implicitHeight: 16
-        radius: slider.width / 2
-        color: Nheko.colors.highlight
-        visible:  alwaysShowSlider || slider.hovered || slider.pressed || Settings.mobileMode
+        x: control.leftPadding + control.visualPosition * background.width
+        y: control.topPadding + control.availableHeight / 2 - height / 2
+        implicitWidth: control.sliderRadius
+        implicitHeight: control.sliderRadius
+        radius: control.sliderRadius/2
+				color: control.progressColor
+        visible:  Settings.mobileMode || control.alwaysShowSlider || control.hovered || control.pressed
+        border.color: control.progressColor
     }
-
 }
diff --git a/resources/qml/ui/media/MediaControls.qml b/resources/qml/ui/media/MediaControls.qml
index b529462da..f5321dca6 100644
--- a/resources/qml/ui/media/MediaControls.qml
+++ b/resources/qml/ui/media/MediaControls.qml
@@ -3,28 +3,33 @@
 // 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
 
-Item {
+Rectangle {
     id: control
 
     property alias desiredVolume: volumeSlider.desiredVolume
-    property alias muted: volumeSlider.muted
+    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 int controlHeight: 25
     property bool shouldShowControls: !playingVideo || playerMouseArea.shouldShowControls || volumeSlider.controlsVisible
+    color: {
+        var wc = Nheko.colors.alternateBase;
+        return Qt.rgba(wc.r, wc.g, wc.b, 0.5);
+    }
+    height: controlLayout.implicitHeight
 
-    signal playPauseActivated(real mouseX, real mouseY)
-    signal loadActivated(real mouseX, real mouseY)
+    signal playPauseActivated()
+    signal loadActivated()
 
     function durationToString(duration) {
         function maybeZeroPrepend(time) {
@@ -51,7 +56,7 @@ Item {
         property bool shouldShowControls: (containsMouse && controlHideTimer.running) || (control.mediaState != MediaPlayer.PlayingState) || controlLayout.contains(mapToItem(controlLayout, mouseX, mouseY))
 
         onClicked: {
-            control.mediaLoaded ? control.playPauseActivated(mouseX, mouseY) : control.loadActivated(mouseX, mouseY);
+            control.mediaLoaded ? control.playPauseActivated() : control.loadActivated();
         }
         hoverEnabled: true
         onPositionChanged: controlHideTimer.start()
@@ -66,102 +71,155 @@ Item {
         id: controlLayout
         opacity: control.shouldShowControls ? 1 : 0
 
-        // spacing: Nheko.paddingSmall
+        spacing: 0
         anchors.bottom: control.bottom
         anchors.left: control.left
         anchors.right: control.right
 
         NhekoSlider {
             Layout.fillWidth: true
-            Layout.minimumWidth: 50
-            Layout.leftMargin: Nheko.paddingMedium
-            Layout.rightMargin: Nheko.paddingMedium
-            height: control.controlHeight
+            Layout.leftMargin: Nheko.paddingSmall
+            Layout.rightMargin: Nheko.paddingSmall
+
+            enabled: control.mediaLoaded
+
             value: control.positionValue
             onMoved: control.position = value
             from: 0
             to: control.duration
-            sliderHeight: 8
             alwaysShowSlider: false
         }
 
-        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);
+        RowLayout {
+            Layout.margins: Nheko.paddingSmall
+            spacing: Nheko.paddingSmall
+            Layout.fillWidth: true
+
+            // Cache/Play/pause button
+            ImageButton {
+                Layout.alignment: Qt.AlignLeft
+                id: playbackStateImage
+
+                buttonTextColor: Nheko.colors.text
+                Layout.preferredHeight: 24
+                Layout.preferredWidth: 24
+
+                image: {
+                    if (control.mediaLoaded) {
+                        if (control.mediaState == MediaPlayer.PlayingState)
+                        return ":/icons/icons/ui/pause-symbol.png";
+                        else
+                        return ":/icons/icons/ui/play-sign.png";
+                    } else {
+                        return ":/icons/icons/ui/arrow-pointing-down.png";
+                    }
+                }
+
+                onClicked: control.mediaLoaded ? control.playPauseActivated() : control.loadActivated();
+
             }
 
-            Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
+            ImageButton {
+                Layout.alignment: Qt.AlignLeft
+                id: volumeButton
 
-            height: 35
-            Layout.fillWidth: true
+                buttonTextColor: Nheko.colors.text
+                Layout.preferredHeight: 24
+                Layout.preferredWidth: 24
 
-            RowLayout {
-                anchors.left: controlRect.left
-                anchors.bottom: controlRect.bottom
-                anchors.right: controlRect.right
-                anchors.margins: Nheko.paddingSmall
-                anchors.verticalCenter: controlRect.verticalCenter
-                spacing: Nheko.paddingSmall
-
-                // Cache/Play/pause button
-                Image {
-                    Layout.alignment: Qt.AlignLeft
-                    id: playbackStateImage
-
-                    property color controlColor: (playbackStateArea.containsMouse) ? Nheko.colors.highlight : Nheko.colors.text
-
-                    fillMode: Image.PreserveAspectFit
-                    Layout.preferredHeight: control.controlHeight
-                    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;
-                        }
+                image: {
+                    if (control.muted || control.desiredVolume <= 0) {
+                        return ":/icons/icons/ui/volume-off-indicator.png";
+                    } else {
+                        return ":/icons/icons/ui/volume-up.png";
                     }
+                }
 
-                    MouseArea {
-                        id: playbackStateArea
+                onClicked: control.muted = !control.muted
 
-                        anchors.fill: parent
-                        hoverEnabled: true
-                        onClicked: {
-                            control.mediaLoaded ? control.playPauseActivated(mouseX, mouseY) : control.loadActivated(mouseX, mouseY);
-                        }
-                    }
+            }
 
-                }
+            NhekoSlider {
+                state: ""
 
-                VolumeControl {
-                    Layout.alignment: Qt.AlignLeft
-                    id: volumeSlider
-                    orientation: Qt.Horizontal
-                    Layout.rightMargin: 5
-                    Layout.preferredHeight: control.controlHeight
+                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
+                Layout.alignment: Qt.AlignLeft
+                Layout.preferredWidth: 0
+                opacity: 0
+                id: volumeSlider
+                orientation: Qt.Horizontal
+                property real desiredVolume: QtMultimedia.convertVolume(volumeSlider.value, QtMultimedia.LogarithmicVolumeScale, QtMultimedia.LinearVolumeScale)
+                value: 1
 
-                    text: (!control.mediaLoaded) ? "-/-" : (durationToString(control.positionValue) + "/" + durationToString(control.duration))
-                    color: Nheko.colors.text
+                onDesiredVolumeChanged: {
+                    control.muted = !(desiredVolume > 0);
                 }
 
-                Item {
-                    Layout.fillWidth: true
-                }
+                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
+                                }
+                            }
+                        }
 
+                    }
+                ]
+            }
+
+            Label {
+                Layout.alignment: Qt.AlignRight
+
+                text: (!control.mediaLoaded) ? "-- / --" : (durationToString(control.positionValue) + " / " + durationToString(control.duration))
+                color: Nheko.colors.text
+            }
+
+            Item {
+                Layout.fillWidth: true
             }
 
         }
 
+
         // Fade controls in/out
         Behavior on opacity {
             OpacityAnimator {
diff --git a/resources/qml/ui/media/VolumeControl.qml b/resources/qml/ui/media/VolumeControl.qml
deleted file mode 100644
index e87550ac4..000000000
--- a/resources/qml/ui/media/VolumeControl.qml
+++ /dev/null
@@ -1,117 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-import "../"
-
-import QtMultimedia 5.15
-import QtQuick 2.15
-import QtQuick.Controls 2.15
-
-import im.nheko 1.0
-
-// Volume slider activator
-Image {
-    // TODO: add icons for different volume levels
-    id: volumeImage
-
-    property alias desiredVolume: volumeSlider.desiredVolume
-    property alias orientation: volumeSlider.orientation
-    property alias controlsVisible: volumeSliderRect.visible
-    property bool muted: false
-    property color controlColor: (volumeImageArea.containsMouse) ? Nheko.colors.highlight : Nheko.colors.text
-    width: sourceSize.width + volumeSliderRect.implicitWidth
-
-    source: (desiredVolume > 0 && !muted) ? "image://colorimage/:/icons/icons/ui/volume-up.png?" + controlColor : "image://colorimage/:/icons/icons/ui/volume-off-indicator.png?" + controlColor
-    fillMode: Image.PreserveAspectFit
-
-    MouseArea {
-        id: volumeImageArea
-
-        anchors.fill: parent
-        hoverEnabled: true
-        onExited: volumeSliderHideTimer.start()
-        onPositionChanged: volumeSliderHideTimer.start()
-        onClicked: volumeImage.muted = !volumeImage.muted
-
-        // For hiding volume slider after a while
-        Timer {
-            id: volumeSliderHideTimer
-
-            interval: 1500
-            repeat: false
-            running: false
-        }
-
-    }
-
-    Rectangle {
-        id: volumeSliderRect
-
-        opacity: (visible) ? 1 : 0
-        anchors.bottom: volumeSlider.orientation == Qt.Vertical ? volumeImage.top : undefined
-        anchors.left: volumeSlider.orientation == Qt.Vertical ? undefined : volumeImage.right
-        anchors.horizontalCenter: volumeSlider.orientation == Qt.Vertical ? volumeImage.horizontalCenter : undefined
-        anchors.verticalCenter: volumeSlider.orientation == Qt.Vertical ? undefined : volumeImage.verticalCenter
-        color: {
-            if (volumeSlider.orientation == Qt.Vertical) {
-                var wc = Nheko.colors.window;
-                return Qt.rgba(wc.r, wc.g, wc.b, 0.5);
-            } else {
-                return "transparent";
-            }
-        }
-        /* TODO: base width on the slider width (some issue with it not having a geometry
-        when using the width here?) */
-        width: volumeSlider.orientation == Qt.Vertical ? volumeImage.width * 0.7 : 100
-        radius: volumeSlider.width / 2
-        height: volumeSlider.orientation == Qt.Vertical ? 100 : volumeImage.height * 0.7
-        visible: volumeImageArea.containsMouse || volumeSliderHideTimer.running || volumeSliderRectMouseArea.containsMouse
-
-        NhekoSlider {
-            // TODO: the slider is slightly off-center on the left for some reason...
-            id: volumeSlider
-
-            sliderWidth: 8
-            sliderHeight: 8
-            // Desired value to avoid loop onMoved -> media.volume -> value -> onMoved...
-            property real desiredVolume: QtMultimedia.convertVolume(volumeSlider.value, QtMultimedia.LogarithmicVolumeScale, QtMultimedia.LinearVolumeScale)
-
-            value: 1
-            anchors.fill: volumeSliderRect
-            anchors.horizontalCenter: orientation == Qt.Vertical ? volumeSliderRect.horizontalCenter : undefined
-            anchors.verticalCenter: orientation == Qt.Vertical ? undefined : volumeSliderRect.verticalCenter
-            orientation: Qt.Vertical
-            onDesiredVolumeChanged: {
-                volumeImage.muted = !(desiredVolume > 0);
-            }
-        }
-        // Used for resetting the timer on mouse moves on volumeSliderRect
-
-        MouseArea {
-            id: volumeSliderRectMouseArea
-
-            anchors.fill: parent
-            hoverEnabled: true
-            propagateComposedEvents: true
-            onExited: volumeSliderHideTimer.start()
-            onClicked: mouse.accepted = false
-            onPressed: mouse.accepted = false
-            onReleased: mouse.accepted = false
-            onPressAndHold: mouse.accepted = false
-            onPositionChanged: {
-                mouse.accepted = false;
-                volumeSliderHideTimer.start();
-            }
-        }
-
-        Behavior on opacity {
-            OpacityAnimator {
-                duration: 100
-            }
-
-        }
-
-    }
-
-}
diff --git a/resources/res.qrc b/resources/res.qrc
index 4e243251c..a60f4ab03 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -185,7 +185,6 @@
         <file>qml/ui/Spinner.qml</file>
         <file>qml/ui/animations/BlinkAnimation.qml</file>
         <file>qml/ui/media/MediaControls.qml</file>
-        <file>qml/ui/media/VolumeControl.qml</file>
         <file>qml/voip/ActiveCallBar.qml</file>
         <file>qml/voip/CallDevices.qml</file>
         <file>qml/voip/CallInvite.qml</file>
-- 
GitLab