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 }