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