linphone-desktop/Linphone/view/Control/Display/Chat/FileView.qml
2026-01-06 15:52:26 +01:00

356 lines
11 KiB
QML

import QtQuick
import QtQuick.Controls as Control
import QtQuick.Layouts
import Linphone
import UtilsCpp
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils
// =============================================================================
Item {
id: mainItem
property ChatMessageContentGui contentGui
property string thumbnail: contentGui && contentGui.core.thumbnail || ""
property string name: contentGui && contentGui.core.name
property string filePath: contentGui && contentGui.core.filePath
property bool wasDownloaded: contentGui && contentGui.core.wasDownloaded
property bool isAnimatedImage : contentGui && contentGui.core.wasDownloaded && UtilsCpp.isAnimatedImage(filePath)
property bool haveThumbnail: contentGui && UtilsCpp.canHaveThumbnail(filePath) && UtilsCpp.fileExists(filePath)
property int fileSize: contentGui ? contentGui.core.fileSize : 0
property bool isTransferring
property bool isVideo: UtilsCpp.isVideo(filePath)
property bool isImage: UtilsCpp.isImage(filePath)
property bool isPdf: UtilsCpp.isPdf(filePath)
property bool isThumbnail: isVideo || isImage || isPdf
// property to change default view display
property bool showAsSquare: true
// default image
property var imageSource: mainItem.contentGui
? UtilsCpp.isImage(mainItem.filePath)
? AppIcons.fileImage
: UtilsCpp.isPdf(mainItem.filePath)
? AppIcons.filePdf
: UtilsCpp.isText(mainItem.filePath)
? AppIcons.fileText
: AppIcons.file
: ''
property var thumbnailFillMode: Image.PreserveAspectCrop
Connections {
enabled: contentGui
target: contentGui ? contentGui.core : null
function onMsgStateChanged(state) {
mainItem.isTransferring = state === LinphoneEnums.ChatMessageState.StateFileTransferInProgress
|| state === LinphoneEnums.ChatMessageState.StateInProgress
}
}
// property bool isHovering: thumbnailProvider.state == 'hovered'
property bool isOutgoing: false
// ---------------------------------------------------------------------
// Thumbnail
// ---------------------------------------------------------------------
Component {
id: thumbnailImage
Item {
id: thumbnailSource
Image {
anchors.fill: parent
visible: mainItem.isPdf
source: AppIcons.filePdf
sourceSize.width: mainItem.width
sourceSize.height: mainItem.height
fillMode: Image.PreserveAspectFit
}
Image {
id: errorImage
anchors.fill: parent
z: image.z + 1
visible: image.status == Image.Error || image.status == Image.Null
source: AppIcons.fileImage
sourceSize.width: mainItem.width
sourceSize.height: mainItem.height
fillMode: Image.PreserveAspectFit
}
Item {
id: loadingImageItem
anchors.fill: parent
visible: image.status === Image.Loading && !image.visible && !errorImage.visilbe
Rectangle {
anchors.fill: parent
color: DefaultStyle.main1_200
opacity: 0.2
}
BusyIndicator {
anchors.centerIn: parent
width: Utils.getSizeWithScreenRatio(20)
}
}
Image {
id: image
visible: mainItem.isImage && status !== Image.Loading
mipmap: false//SettingsModel.mipmapEnabled
source: mainItem.thumbnail
sourceSize.width: mainItem.width
sourceSize.height: mainItem.height
autoTransform: true
anchors.fill: parent
fillMode: mainItem.thumbnailFillMode
}
Rectangle {
visible: mainItem.isVideo
color: DefaultStyle.grey_1000
anchors.fill: parent
// Video {
// id: videoThumbnail
// anchors.fill: parent
// position: 100
// source: mainItem.isVideo ? "file:///" + mainItem.filePath : ""
// fillMode: playbackState === MediaPlayer.PlayingState ? VideoOutput.PreserveAspectFit : VideoOutput.PreserveAspectCrop
EffectImage {
anchors.centerIn: parent
// visible: videoThumbnail.playbackState !== MediaPlayer.PlayingState
width: Utils.getSizeWithScreenRatio(24)
height: Utils.getSizeWithScreenRatio(24)
imageSource: AppIcons.playFill
colorizationColor: DefaultStyle.main2_0
}
// Text {
// z: parent.z + 1
// property int timeDisplayed: videoThumbnail.playbackState === MediaPlayer.PlayingState ? videoThumbnail.position : videoThumbnail.duration
// anchors.bottom: parent.bottom
// anchors.left: parent.left
// anchors.bottomMargin: Utils.getSizeWithScreenRatio(6)
// anchors.leftMargin: Utils.getSizeWithScreenRatio(6)
// text: UtilsCpp.formatDuration(timeDisplayed)
// color: DefaultStyle.grey_0
// font {
// pixelSize: Typography.d1.pixelSize
// weight: Typography.d1.weight
// }
// }
// }
}
}
}
Component {
id: animatedImage
AnimatedImage {
id: animatedImageSource
mipmap: false//SettingsModel.mipmapEnabled
source: 'file:/'+ mainItem.filePath
autoTransform: true
fillMode: Image.PreserveAspectFit
}
}
// ---------------------------------------------------------------------
// Default view
// ---------------------------------------------------------------------
Component {
id: defaultSquareView
Control.Control {
leftPadding: Utils.getSizeWithScreenRatio(4)
rightPadding: Utils.getSizeWithScreenRatio(4)
topPadding: Utils.getSizeWithScreenRatio(23)
bottomPadding: Utils.getSizeWithScreenRatio(4)
hoverEnabled: false
background: Rectangle {
anchors.fill: parent
color: FileViewStyle.extension.background.color
radius: FileViewStyle.extension.radius
Rectangle {
color: DefaultStyle.main2_200
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: Utils.getSizeWithScreenRatio(23)
EffectImage {
anchors.centerIn: parent
imageSource: mainItem.imageSource
imageWidth: Utils.getSizeWithScreenRatio(14)
imageHeight: Utils.getSizeWithScreenRatio(14)
colorizationColor: DefaultStyle.main2_600
}
}
}
contentItem: Item {
Text {
id: fileName
visible: !progressBar.visible
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
// visible: mainItem.contentGui && !mainItem.isAnimatedImage
font.pixelSize: Typography.f1.pixelSize
font.weight: Typography.f1l.weight
wrapMode: Text.WrapAnywhere
maximumLineCount: 2
text: mainItem.name
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
Text {
id: fileSizeText
visible: !progressBar.visible
anchors.bottom: parent.bottom
anchors.right: parent.right
text: Utils.formatSize(mainItem.fileSize)
font.pixelSize: Typography.f1l.pixelSize
font.weight: Typography.f1l.weight
}
RoundProgressBar {
id: progressBar
anchors.centerIn: parent
to: 100
value: mainItem.contentGui ? (mainItem.fileSize>0 ? Math.floor(100 * mainItem.contentGui.core.fileOffset / mainItem.fileSize) : 0) : to
visible: mainItem.isTransferring && value != 0
/* Change format? Current is %
text: if(mainRow.contentGui){
var mainItem.fileSize = Utils.formatSize(mainRow.contentGui.core.mainItem.fileSize)
return progressBar.visible
? Utils.formatSize(mainRow.contentGui.core.fileOffset) + '/' + mainItem.fileSize
: mainItem.fileSize
}else
return ''
*/
}
Rectangle {
visible: thumbnailProvider.state === 'hovered' && mainItem.contentGui && (/*!mainItem.isOutgoing &&*/ !mainItem.contentGui.core.wasDownloaded)
color: DefaultStyle.grey_0
opacity: 0.5
anchors.fill: parent
}
EffectImage {
visible: thumbnailProvider.state === 'hovered' && mainItem.contentGui && (/*!mainItem.isOutgoing &&*/ !mainItem.contentGui.core.wasDownloaded)
anchors.centerIn: parent
imageSource: AppIcons.download
width: Utils.getSizeWithScreenRatio(24)
height: Utils.getSizeWithScreenRatio(24)
colorizationColor: DefaultStyle.main2_600
}
}
}
}
Component {
id: defaultView
Control.Control {
rightPadding: Utils.getSizeWithScreenRatio(17)
background: Rectangle {
id: bg
color: DefaultStyle.grey_100
width: mainItem.width
height: mainItem.height
radius: Utils.getSizeWithScreenRatio(10)
}
contentItem: RowLayout {
spacing: Utils.getSizeWithScreenRatio(16)
Rectangle {
color: DefaultStyle.main2_200
width: Utils.getSizeWithScreenRatio(58)
height: bg.height
radius: bg.radius
Rectangle {
anchors.right: parent.right
color: DefaultStyle.main2_200
width: parent.width / 2
height: parent.height
radius: parent.radius
}
EffectImage {
z: parent.z + 1
anchors.centerIn: parent
imageSource: defaultViewStack.imageSource
width: Utils.getSizeWithScreenRatio(22)
height: width
colorizationColor: DefaultStyle.main2_600
}
}
ColumnLayout {
spacing: Utils.getSizeWithScreenRatio(1)
Text {
text: mainItem.name
Layout.fillWidth: true
font {
pixelSize: Typography.p2.pixelSize
weight: Typography.p2.weight
}
}
Text {
text: mainItem.fileSize
Layout.fillWidth: true
font {
pixelSize: Typography.p4.pixelSize
weight: Typography.p4.weight
}
}
}
}
}
}
Loader {
id: thumbnailProvider
anchors.fill: parent
sourceComponent: mainItem.contentGui
? mainItem.isAnimatedImage
? animatedImage
: mainItem.haveThumbnail
? thumbnailImage
: mainItem.showAsSquare
? defaultSquareView
: defaultView
: undefined
states: State {
name: 'hovered'
}
MouseArea {
anchors.fill: thumbnailProvider.item
hoverEnabled: true
propagateComposedEvents: true
cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor
// Changing cursor in MouseArea seems not to work with the Loader
// Use override cursor for this case
onContainsMouseChanged: {
if (containsMouse) UtilsCpp.setGlobalCursor(Qt.PointingHandCursor)
else UtilsCpp.restoreGlobalCursor()
thumbnailProvider.state = containsMouse ? 'hovered' : ''
}
onPressed: (mouse) => {
mouse.accepted = false
if(mainItem.isTransferring) {
mainItem.contentGui.core.lCancelDownloadFile()
mouse.accepted = true
}
else if(!mainItem.contentGui.core.wasDownloaded) {
mouse.accepted = true
mainItem.contentGui.core.lDownloadFile()
} else if (Utils.pointIsInItem(this, thumbnailProvider, mouse)) {
mouse.accepted = true
// if(SettingsModel.isVfsEncrypted){
// window.attachVirtualWindow(Utils.buildCommonDialogUri('FileViewDialog'), {
// contentGui: mainItem.contentGui,
// }, function (status) {
// })
// }else
mainItem.contentGui.core.lOpenFile()
} else if (mainItem.contentGui) {
mouse.accepted = true
mainItem.contentGui.core.lOpenFile(true)// Show directory
}
}
}
}
}