diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml
index 2b4c4d49..67214dd7 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 887cb80c..6cf1fd2d 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 b529462d..f5321dca 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);
- }
-
- Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
-
- height: 35
+ RowLayout {
+ Layout.margins: Nheko.paddingSmall
+ spacing: Nheko.paddingSmall
Layout.fillWidth: true
- 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
+ ImageButton {
+ Layout.alignment: Qt.AlignLeft
+ id: playbackStateImage
- // Cache/Play/pause button
- Image {
- Layout.alignment: Qt.AlignLeft
- id: playbackStateImage
+ buttonTextColor: Nheko.colors.text
+ Layout.preferredHeight: 24
+ Layout.preferredWidth: 24
- 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.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";
}
+ }
- MouseArea {
- id: playbackStateArea
+ onClicked: control.mediaLoaded ? control.playPauseActivated() : control.loadActivated();
- anchors.fill: parent
- hoverEnabled: true
- onClicked: {
- control.mediaLoaded ? control.playPauseActivated(mouseX, mouseY) : control.loadActivated(mouseX, mouseY);
- }
+ }
+
+ ImageButton {
+ Layout.alignment: Qt.AlignLeft
+ id: volumeButton
+
+ buttonTextColor: Nheko.colors.text
+ Layout.preferredHeight: 24
+ Layout.preferredWidth: 24
+
+ image: {
+ if (control.muted || control.desiredVolume <= 0) {
+ return ":/icons/icons/ui/volume-off-indicator.png";
+ } else {
+ return ":/icons/icons/ui/volume-up.png";
}
-
}
- VolumeControl {
- Layout.alignment: Qt.AlignLeft
- id: volumeSlider
- orientation: Qt.Horizontal
- Layout.rightMargin: 5
- Layout.preferredHeight: control.controlHeight
+ onClicked: control.muted = !control.muted
+
+ }
+
+ NhekoSlider {
+ state: ""
+
+ 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 e87550ac..00000000
--- 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 4e243251..a60f4ab0 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -185,7 +185,6 @@
qml/ui/Spinner.qml
qml/ui/animations/BlinkAnimation.qml
qml/ui/media/MediaControls.qml
- qml/ui/media/VolumeControl.qml
qml/voip/ActiveCallBar.qml
qml/voip/CallDevices.qml
qml/voip/CallInvite.qml