Use video delegate for videos

There is still two issues:

* Videos send by neochat are broken (missing metadata)
* Once the video has run it is unavailable

Fix #120
This commit is contained in:
Carl Schwan
2020-12-04 10:35:03 +01:00
parent 5ee0ffa144
commit b90a027c99
2 changed files with 161 additions and 155 deletions

View File

@@ -13,27 +13,21 @@ import Qt.labs.platform 1.0 as Platform
import org.kde.kirigami 2.13 as Kirigami import org.kde.kirigami 2.13 as Kirigami
import org.kde.neochat 1.0 import org.kde.neochat 1.0
import NeoChat.Setting 1.0
import NeoChat.Component 1.0 import NeoChat.Component 1.0
import NeoChat.Dialog 1.0 import NeoChat.Dialog 1.0
import NeoChat.Menu.Timeline 1.0 import NeoChat.Menu.Timeline 1.0
import NeoChat.Effect 1.0 import NeoChat.Effect 1.0
import NeoChat.Font 1.0
RowLayout { Video {
id: vid
property bool openOnFinished: false property bool openOnFinished: false
property bool playOnFinished: false property bool playOnFinished: false
readonly property bool downloaded: progressInfo && progressInfo.completed readonly property bool downloaded: progressInfo && progressInfo.completed
property bool supportStreaming: true property bool supportStreaming: true
id: root
spacing: 4
z: -5
onDownloadedChanged: { onDownloadedChanged: {
if (downloaded) { if (downloaded) {
vid.source = progressInfo.localPath vid.source = progressInfo.localPath
@@ -49,180 +43,168 @@ RowLayout {
} }
} }
Video {
readonly property int maxWidth: 1000 // TODO messageListView.width
Layout.preferredWidth: content.info.w > maxWidth ? maxWidth : content.info.w readonly property int maxWidth: 1000 // TODO messageListView.width
Layout.preferredHeight: content.info.w > maxWidth ? (content.info.h / content.info.w * maxWidth) : content.info.h
id: vid Layout.preferredWidth: content.info.w > maxWidth ? maxWidth : content.info.w
Layout.preferredHeight: content.info.w > maxWidth ? (content.info.h / content.info.w * maxWidth) : content.info.h
loops: MediaPlayer.Infinite loops: MediaPlayer.Infinite
fillMode: VideoOutput.PreserveAspectFit fillMode: VideoOutput.PreserveAspectFit
Component.onCompleted: { Component.onCompleted: {
if (downloaded) { if (downloaded) {
source = progressInfo.localPath source = progressInfo.localPath
} else { } else {
source = currentRoom.urlToMxcUrl(content.url) source = currentRoom.urlToMxcUrl(content.url)
}
}
onDurationChanged: {
if (!duration) {
supportStreaming = false;
}
}
onErrorChanged: {
if (error != MediaPlayer.NoError) {
supportStreaming = false;
}
}
Image {
readonly property bool isThumbnail: content.info.thumbnail_info && content.thumbnailMediaId
readonly property var info: isThumbnail ? content.info.thumbnail_info : content.info
anchors.fill: parent
visible: isThumbnail && (vid.playbackState == MediaPlayer.StoppedState || vid.error != MediaPlayer.NoError)
source: "image://mxc/" + (isThumbnail ? content.thumbnailMediaId : "")
sourceSize.width: info.w
sourceSize.height: info.h
fillMode: Image.PreserveAspectFit
}
Label {
anchors.centerIn: parent
visible: vid.playbackState == MediaPlayer.StoppedState || vid.error != MediaPlayer.NoError
color: "white"
text: "Video"
font.pixelSize: 16
padding: 8
background: Rectangle {
radius: Kirigami.Units.smallSpacing
color: "black"
opacity: 0.3
}
}
Control {
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
anchors.right: parent.right
anchors.rightMargin: 8
horizontalPadding: 8
verticalPadding: 4
contentItem: RowLayout {
Label {
text: Qt.formatTime(time)
color: "white"
font.pixelSize: 12
}
Label {
text: author.displayName
color: "white"
font.pixelSize: 12
} }
} }
onDurationChanged: { background: Rectangle {
if (!duration) { radius: height / 2
supportStreaming = false; color: "black"
} opacity: 0.3
} }
}
onErrorChanged: { Rectangle {
if (error != MediaPlayer.NoError) { anchors.fill: parent
supportStreaming = false;
}
}
layer.enabled: true visible: progressInfo.active && !downloaded
layer.effect: OpacityMask {
maskSource: Rectangle {
width: vid.width
height: vid.height
radius: 18
}
}
Image { color: "#BB000000"
readonly property bool isThumbnail: content.info.thumbnail_info && content.thumbnailMediaId
readonly property var info: isThumbnail ? content.info.thumbnail_info : content.info
anchors.fill: parent ProgressBar {
visible: isThumbnail && (vid.playbackState == MediaPlayer.StoppedState || vid.error != MediaPlayer.NoError)
source: "image://mxc/" + (isThumbnail ? content.thumbnailMediaId : "")
sourceSize.width: info.w
sourceSize.height: info.h
fillMode: Image.PreserveAspectCrop
}
Label {
anchors.centerIn: parent anchors.centerIn: parent
visible: vid.playbackState == MediaPlayer.StoppedState || vid.error != MediaPlayer.NoError width: parent.width * 0.8
color: "white"
text: "Video"
font.pixelSize: 16
padding: 8 from: 0
to: progressInfo.total
background: Rectangle { value: progressInfo.progress
radius: height / 2
color: "black"
opacity: 0.3
}
} }
}
Control { AutoMouseArea {
anchors.bottom: parent.bottom anchors.fill: parent
anchors.bottomMargin: 8
anchors.right: parent.right
anchors.rightMargin: 8
horizontalPadding: 8 id: messageMouseArea
verticalPadding: 4
contentItem: RowLayout { onPrimaryClicked: {
Label { if (supportStreaming || progressInfo.completed) {
text: Qt.formatTime(time) if (vid.playbackState == MediaPlayer.PlayingState) {
color: "white" vid.pause()
font.pixelSize: 12
}
Label {
text: author.displayName
color: "white"
font.pixelSize: 12
}
}
background: Rectangle {
radius: height / 2
color: "black"
opacity: 0.3
}
}
Rectangle {
anchors.fill: parent
visible: progressInfo.active && !downloaded
color: "#BB000000"
ProgressBar {
anchors.centerIn: parent
width: parent.width * 0.8
from: 0
to: progressInfo.total
value: progressInfo.progress
}
}
RippleEffect {
anchors.fill: parent
id: messageMouseArea
onPrimaryClicked: {
if (supportStreaming || progressInfo.completed) {
if (vid.playbackState == MediaPlayer.PlayingState) {
vid.pause()
} else {
vid.play()
}
} else { } else {
downloadAndPlay() vid.play()
} }
} else {
downloadAndPlay()
} }
}
onSecondaryClicked: { onSecondaryClicked: {
var contextMenu = imageDelegateContextMenu.createObject(root) var contextMenu = imageDelegateContextMenu.createObject(vid)
contextMenu.viewSource.connect(function() { contextMenu.viewSource.connect(function() {
messageSourceSheet.createObject(ApplicationWindow.overlay, {"sourceText": toolTip}).open() messageSourceSheet.createObject(ApplicationWindow.overlay, {"sourceText": toolTip}).open()
}) })
contextMenu.downloadAndOpen.connect(downloadAndOpen) contextMenu.downloadAndOpen.connect(downloadAndOpen)
contextMenu.saveFileAs.connect(saveFileAs) contextMenu.saveFileAs.connect(saveFileAs)
contextMenu.reply.connect(function() { contextMenu.reply.connect(function() {
roomPanelInput.replyModel = Object.assign({}, model) roomPanelInput.replyModel = Object.assign({}, model)
roomPanelInput.isReply = true roomPanelInput.isReply = true
roomPanelInput.focus() roomPanelInput.focus()
}) })
contextMenu.redact.connect(function() { contextMenu.redact.connect(function() {
currentRoom.redactEvent(eventId) currentRoom.redactEvent(eventId)
}) })
contextMenu.popup() contextMenu.popup()
} }
Component { Component {
id: messageSourceSheet id: messageSourceSheet
MessageSourceSheet {} MessageSourceSheet {}
} }
Component { Component {
id: openFolderDialog id: openFolderDialog
OpenFolderDialog {} OpenFolderDialog {}
} }
Component { Component {
id: imageDelegateContextMenu id: imageDelegateContextMenu
FileDelegateContextMenu {} FileDelegateContextMenu {}
}
} }
} }

View File

@@ -343,6 +343,7 @@ Kirigami.ScrollablePage {
ReactionDelegate { ReactionDelegate {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 0 Layout.topMargin: 0
Layout.maximumHeight: 320
Layout.bottomMargin: 8 Layout.bottomMargin: 8
} }
] ]
@@ -358,6 +359,16 @@ Kirigami.ScrollablePage {
innerObject: MessageDelegate { innerObject: MessageDelegate {
Layout.fillWidth: true Layout.fillWidth: true
onReplyClicked: goToEvent(eventID) onReplyClicked: goToEvent(eventID)
mouseArea: MouseArea {
acceptedButtons: (Kirigami.Settings.isMobile ? Qt.LeftButton : 0) | Qt.RightButton
anchors.fill: parent
onClicked: {
if (mouse.button == Qt.RightButton) {
openMessageContext(author, display, eventId, toolTip);
}
}
onPressAndHold: openMessageContext(author, display, eventId, toolTip);
}
onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId); onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId);
innerObject: AudioDelegate { innerObject: AudioDelegate {
@@ -375,10 +386,23 @@ Kirigami.ScrollablePage {
innerObject: MessageDelegate { innerObject: MessageDelegate {
Layout.fillWidth: true Layout.fillWidth: true
onReplyClicked: goToEvent(eventID) onReplyClicked: goToEvent(eventID)
mouseArea: MouseArea {
acceptedButtons: (Kirigami.Settings.isMobile ? Qt.LeftButton : 0) | Qt.RightButton
anchors.fill: parent
onClicked: {
if (mouse.button == Qt.RightButton) {
openMessageContext(author, display, eventId, toolTip);
}
}
onPressAndHold: openMessageContext(author, display, eventId, toolTip);
}
onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId); onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId);
innerObject: AudioDelegate { innerObject: VideoDelegate {
Layout.fillWidth: true Layout.maximumWidth: parent.width
Layout.minimumWidth: 320
Layout.maximumHeight: 320
Layout.preferredHeight: content.info.h / content.info.w * width
} }
} }
} }