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 }