Warning, /libraries/kirigami-addons/src/components/VideoMaximizeDelegate.qml is written in an unsupported language. File is not indexed.

0001 // SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
0002 // SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0003 
0004 import QtQuick
0005 import QtQuick.Controls as QQC2
0006 import QtQuick.Layouts
0007 import QtMultimedia
0008 
0009 import org.kde.kirigami as Kirigami
0010 
0011 Item {
0012     id: root
0013 
0014     /**
0015      * @brief The source for the image to be viewed.
0016      */
0017     required property string source
0018 
0019     /**
0020      * @brief Source for the temporary content.
0021      *
0022      * Typically used when downloading the image to show a thumbnail or other
0023      * temporary image while the main image downloads.
0024      */
0025     required property string tempSource
0026 
0027     /**
0028      * @brief The size of the source image.
0029      *
0030      * This is used to calculate the maximum size of the content and temporary image.
0031      */
0032     required property real sourceWidth
0033 
0034     /**
0035      * @brief The size of the source image.
0036      *
0037      * This is used to calculate the maximum size of the content and temporary image.
0038      */
0039     required property real sourceHeight
0040 
0041     /**
0042      * @brief The caption for the item.
0043      *
0044      * Typically set to the filename if no caption is available.
0045      *
0046      * @note Declared here so that parent components can access this parameter
0047      *       when used as a listView delegate.
0048      */
0049     required property string caption
0050 
0051     /**
0052      * @brief The delegate type for this item.
0053      *
0054      * @note Declared here so that parent components can access this parameter
0055      *       when used as a listView delegate.
0056      */
0057     readonly property int type: AlbumModelItem.Video
0058 
0059     /**
0060      * @brief Whether the source video should auto-load.
0061      *
0062      * @deprecated due to changes in the Video API this will be removed in KF6. It
0063      *             currently does nothing but is kept to avoid breakage. The loss
0064      *             of this API has been worked around in a way that doesn't break KF5.
0065      */
0066     property bool autoLoad
0067 
0068     /**
0069      * @brief Whether the source video should auto-play.
0070      */
0071     property bool autoPlay
0072 
0073     /**
0074      * @brief The default action triggered when the download button is pressed.
0075      *
0076      * This exists as a property so that the action can be overridden. The most common
0077      * use case for this is where a custom URI scheme is used.
0078      */
0079     property DownloadAction downloadAction: DownloadAction {
0080         onTriggered: videoItem.play()
0081     }
0082 
0083     /**
0084      * @brief The padding around the content image.
0085      *
0086      * The padding is factored in when calculating the maximum size of the content
0087      * image.
0088      */
0089     property var padding: Kirigami.Units.largeSpacing
0090 
0091     /**
0092      * @brief Multiple by which the image is scaled.
0093      */
0094     property var scaleFactor: 1
0095 
0096     /**
0097      * @brief Emitted when the background space around the content item is clicked.
0098      */
0099     signal backgroundClicked()
0100 
0101     /**
0102      * @brief Emitted when the content image is right clicked.
0103      */
0104     signal itemRightClicked()
0105 
0106     /**
0107      * @brief Start media playback.
0108      */
0109     function play() {
0110         videoItem.play()
0111     }
0112 
0113     /**
0114      * @brief Pause media playback.
0115      */
0116     function pause() {
0117         videoItem.pause()
0118     }
0119 
0120     clip: true
0121 
0122     Video {
0123         id: videoItem
0124 
0125         anchors.centerIn: parent
0126         width: {
0127             if (root.sourceWidth > 0 ) {
0128                 return Math.min(root.sourceWidth, root.width - root.padding * 2)
0129             } else if (metaData.resolution && metaData.resolution.width) {
0130                 return Math.min(metaData.resolution.width, root.width - root.padding * 2)
0131             } else {
0132                 return 0
0133             }
0134         }
0135         height: {
0136             if (root.sourceHeight > 0 ) {
0137                 return Math.min(root.sourceHeight, root.height - root.padding * 2)
0138             } else if (metaData.resolution && metaData.resolution.height) {
0139                 return Math.min(metaData.resolution.height, root.height - root.padding * 2)
0140             } else {
0141                 return 0
0142             }
0143         }
0144 
0145         source: root.source
0146         onSourceChanged: {
0147             if (source.toString().length > 0 && root.autoPlay) {
0148                 videoItem.play()
0149             }
0150         }
0151 
0152         clip: true
0153 
0154         Behavior on width {
0155             NumberAnimation {duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic}
0156         }
0157         Behavior on height {
0158             NumberAnimation {duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic}
0159         }
0160 
0161         Image {
0162             id: tempImage
0163             anchors.centerIn: parent
0164             width: root.sourceWidth > 0 || (videoItem.metaData.resolution && videoItem.metaData.resolution.width > 0) ? root.sourceWidth : tempImage.sourceSize.width
0165             height: root.sourceHeight > 0 || (videoItem.metaData.resolution && videoItem.metaData.resolution.height > 0) ? root.sourceHeight : tempImage.sourceSize.height
0166             visible: source && status === Image.Ready && !videoItem.source.toString().length > 0
0167 
0168             source: root.tempSource
0169         }
0170 
0171         QQC2.ProgressBar {
0172             anchors.centerIn: parent
0173             visible: root.downloadAction.started && !root.downloadAction.completed
0174             width: videoItem.width * 0.8
0175 
0176             from: 0.0
0177             to: 100.0
0178             value: root.downloadAction.progress
0179         }
0180 
0181         QQC2.Button {
0182             anchors.centerIn: parent
0183             icon.width: Kirigami.Units.iconSizes.large
0184             icon.height: Kirigami.Units.iconSizes.large
0185             visible: !videoItem.source.toString().length > 0 && !root.downloadAction.started
0186             display: QQC2.AbstractButton.IconOnly
0187             action: root.downloadAction
0188         }
0189 
0190         transform: [
0191             Scale {
0192                 origin.x: videoItem.width / 2
0193                 origin.y: videoItem.height / 2
0194                 xScale: root.scaleFactor
0195                 yScale: root.scaleFactor
0196 
0197                 Behavior on xScale {
0198                     NumberAnimation {duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic}
0199                 }
0200                 Behavior on yScale {
0201                     NumberAnimation {duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic}
0202                 }
0203             }
0204         ]
0205 
0206         QQC2.Control {
0207             id: videoControls
0208             anchors.bottom: videoItem.bottom
0209             anchors.left: videoItem.left
0210             anchors.right: videoItem.right
0211             visible: videoArea.hovered || volumePopupHoverHandler.hovered || volumeSlider.hovered || videoControlTimer.running
0212 
0213             contentItem: RowLayout {
0214                 id: controlRow
0215                 QQC2.ToolButton {
0216                     id: playButton
0217                     z: 1
0218                     icon.name: videoItem.playbackState === MediaPlayer.PlayingState ? "media-playback-pause" : "media-playback-start"
0219                     onClicked: videoItem.playbackState === MediaPlayer.PlayingState ? videoItem.pause() : videoItem.play()
0220                 }
0221                 QQC2.Slider {
0222                     Layout.fillWidth: true
0223                     from: 0
0224                     to: videoItem.duration
0225                     value: videoItem.position
0226                     onMoved: videoItem.seek(value)
0227                 }
0228                 QQC2.Label {
0229                     text: root.getTimeString(videoItem.position) + "/" + root.getTimeString(videoItem.duration)
0230                 }
0231                 QQC2.ToolButton {
0232                     id: volumeButton
0233                     property var unmuteVolume: videoItem.volume
0234 
0235                     icon.name: videoItem.volume <= 0 ? "player-volume-muted" : "player-volume"
0236 
0237                     QQC2.ToolTip.visible: hovered
0238                     QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
0239                     QQC2.ToolTip.timeout: Kirigami.Units.toolTipDelay
0240                     QQC2.ToolTip.text: i18ndc("kirigami-addons6", "@action:button", "Volume")
0241 
0242                     onClicked: {
0243                         if (videoItem.volume > 0) {
0244                             videoItem.volume = 0
0245                         } else {
0246                             if (unmuteVolume === 0) {
0247                                 videoItem.volume = 1
0248                             } else {
0249                                 videoItem.volume = unmuteVolume
0250                             }
0251                         }
0252                     }
0253                     onHoveredChanged: {
0254                         if (!hovered) {
0255                             videoControlTimer.restart()
0256                             volumePopupTimer.restart()
0257                         }
0258                     }
0259 
0260                     QQC2.Popup {
0261                         id: volumePopup
0262                         y: -height
0263                         width: volumeButton.width
0264                         visible: volumeButton.hovered || volumePopupHoverHandler.hovered || volumeSlider.hovered || volumePopupTimer.running
0265 
0266                         focus: true
0267                         padding: Kirigami.Units.smallSpacing
0268                         closePolicy: QQC2.Popup.NoAutoClose
0269 
0270                         QQC2.Slider {
0271                             id: volumeSlider
0272                             anchors.centerIn: parent
0273                             implicitHeight: Kirigami.Units.gridUnit * 7
0274                             orientation: Qt.Vertical
0275                             padding: 0
0276                             from: 0
0277                             to: 1
0278                             value: videoItem.volume
0279                             onMoved: {
0280                                 videoItem.volume = value
0281                                 volumeButton.unmuteVolume = value
0282                             }
0283                             onHoveredChanged: {
0284                                 if (!hovered) {
0285                                     videoControlTimer.restart()
0286                                     volumePopupTimer.restart()
0287                                 }
0288                             }
0289                         }
0290                         Timer {
0291                             id: volumePopupTimer
0292                             interval: 500
0293                         }
0294                         HoverHandler {
0295                             id: volumePopupHoverHandler
0296                             onHoveredChanged: {
0297                                 if (!hovered) {
0298                                     videoControlTimer.restart()
0299                                     volumePopupTimer.restart()
0300                                 }
0301                             }
0302                         }
0303                         background: Kirigami.ShadowedRectangle {
0304                             radius: 4
0305                             color: Kirigami.Theme.backgroundColor
0306                             opacity: 0.8
0307 
0308                             border.color:  Kirigami.ColorUtils.linearInterpolation(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, Kirigami.Theme.frameContrast)
0309                             border.width: 1
0310 
0311                             shadow.xOffset: 0
0312                             shadow.yOffset: 4
0313                             shadow.color: Qt.rgba(0, 0, 0, 0.3)
0314                             shadow.size: 8
0315                         }
0316                     }
0317                 }
0318             }
0319             background: Kirigami.ShadowedRectangle {
0320                 radius: 4
0321                 color: Kirigami.Theme.backgroundColor
0322                 opacity: 0.8
0323 
0324                 border.color:  Kirigami.ColorUtils.linearInterpolation(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, Kirigami.Theme.frameContrast)
0325                 border.width: 1
0326 
0327                 shadow.xOffset: 0
0328                 shadow.yOffset: 4
0329                 shadow.color: Qt.rgba(0, 0, 0, 0.3)
0330                 shadow.size: 8
0331             }
0332         }
0333         Timer {
0334             id: videoControlTimer
0335             interval: 1000
0336         }
0337 
0338         TapHandler {
0339             acceptedButtons: Qt.RightButton
0340             onTapped: root.itemRightClicked()
0341         }
0342         HoverHandler {
0343             id: videoArea
0344             onHoveredChanged: {
0345                 if (!hovered) {
0346                     videoControlTimer.restart()
0347                 }
0348             }
0349         }
0350     }
0351     TapHandler {
0352         acceptedButtons: Qt.LeftButton
0353         onTapped: root.backgroundClicked()
0354     }
0355 
0356     function formatTimer(time){
0357         return (time < 10 ? "0" : "") + time
0358     }
0359 
0360     function getTimeString(timeMilliseconds){
0361         let hours = root.formatTimer(Math.floor(timeMilliseconds/3600000));
0362         let minutes = root.formatTimer(Math.floor(timeMilliseconds%3600000/60000));
0363         let seconds = root.formatTimer(Math.floor(timeMilliseconds%60000/1000));
0364 
0365         return (hours + ":" + minutes + ":" + seconds);
0366     }
0367 }