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 }