Warning, /maui/mauikit/src/controls.6/AnimatedImageViewer.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 * SPDX-FileCopyrightText: (C) 2015 Vishesh Handa <vhanda@kde.org> 0003 * SPDX-FileCopyrightText: (C) 2017 Atul Sharma <atulsharma406@gmail.com> 0004 * SPDX-FileCopyrightText: (C) 2017 Marco Martin <mart@kde.org> 0005 * 0006 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0007 */ 0008 0009 import QtQuick 0010 import QtQuick.Controls 0011 import org.mauikit.controls 1.3 as Maui 0012 0013 /** 0014 * @brief A view for displaying animated images, such as GIF documents. 0015 * 0016 * <a href="https://doc.qt.io/qt-6/qml-qtquick-controls-flickable.html">This controls inherits from QQC2 Flickable, to checkout its inherited properties refer to the Qt Docs.</a> 0017 * 0018 * This control along with the ImageViewer are meant to display images and support to zoom in and out with touch and mouse gestures and keyboard shortcuts. 0019 * 0020 */ 0021 Flickable 0022 { 0023 id: flick 0024 0025 contentWidth: width 0026 contentHeight: height 0027 boundsBehavior: Flickable.StopAtBounds 0028 boundsMovement: Flickable.StopAtBounds 0029 interactive: contentWidth > width || contentHeight > height 0030 clip: true 0031 0032 ScrollBar.vertical: ScrollBar { 0033 visible: false 0034 } 0035 ScrollBar.horizontal: ScrollBar { 0036 visible: false 0037 } 0038 0039 /** 0040 * @brief This an alias to the actual control painting the image. 0041 * This control is handled by a QQC2 AnimatedImage. 0042 * @property AnimatedImage AnimatedImageViewer::image 0043 */ 0044 property alias image: image 0045 0046 /** 0047 * @brief The painted size of the image. 0048 * As taken from Qt documentation: This property holds the scaled width and height of the full-frame image. 0049 * Unlike the width and height properties, which scale the painting of the image, this property sets the maximum number of pixels stored for the loaded image so that large images do not use more memory than necessary. 0050 * @property size AnimatedImageViewer::sourceSize 0051 */ 0052 property alias sourceSize : image.sourceSize 0053 0054 /** 0055 * @brief The fill mode of the image. The possible values can be found on the Image control documentation from Qt. 0056 * By default this is set to `Image.PreserveAspectFit`. 0057 * @property enumaration AnimatedImageViewer::fillMode 0058 */ 0059 property alias fillMode: image.fillMode 0060 0061 /** 0062 * @brief Whether the image should be loaded asynchronously. 0063 * By default this is set to `true`. 0064 * @property bool AnimatedImageViewer::asynchronous 0065 */ 0066 property alias asynchronous : image.asynchronous 0067 0068 /** 0069 * @brief If the image should be cached in memory. 0070 * The default value is set to `true` 0071 * @property bool AnimatedImageViewer::cache 0072 */ 0073 property alias cache: image.cache 0074 0075 /** 0076 * @brief The painted width of the image. This the same as using the image `sourceSize` property to set the width. 0077 * @property int AnimatedImageViewer::imageWidth 0078 */ 0079 property alias imageWidth: image.sourceSize.width 0080 0081 /** 0082 * @brief The painted height of the image. This the same as using the image `sourceSize` property to set the height. 0083 * @property int AnimatedImageViewer::imageHeight 0084 */ 0085 property alias imageHeight: image.sourceSize.height 0086 0087 /** 0088 * @brief The source of the image. Can be a remote or local file URL. 0089 * @property url AnimatedImageViewer::source 0090 */ 0091 property alias source : image.source 0092 0093 /** 0094 * @brief Emitted when the image area has been right clicked with a mouse event. 0095 */ 0096 signal rightClicked() 0097 0098 /** 0099 * @brief Emitted when the image area has been pressed for a few seconds. 0100 */ 0101 signal pressAndHold() 0102 0103 PinchArea { 0104 width: Math.max(flick.contentWidth, flick.width) 0105 height: Math.max(flick.contentHeight, flick.height) 0106 0107 property real initialWidth 0108 property real initialHeight 0109 0110 onPinchStarted: { 0111 initialWidth = flick.contentWidth 0112 initialHeight = flick.contentHeight 0113 } 0114 0115 onPinchUpdated: { 0116 // adjust content pos due to drag 0117 flick.contentX += pinch.previousCenter.x - pinch.center.x 0118 flick.contentY += pinch.previousCenter.y - pinch.center.y 0119 0120 // resize content 0121 flick.resizeContent(Math.max(flick.width*0.7, initialWidth * pinch.scale), Math.max(flick.height*0.7, initialHeight * pinch.scale), pinch.center) 0122 } 0123 0124 onPinchFinished: { 0125 // Move its content within bounds. 0126 if (flick.contentWidth < flick.width || 0127 flick.contentHeight < flick.height) { 0128 zoomAnim.x = 0; 0129 zoomAnim.y = 0; 0130 zoomAnim.width = flick.width; 0131 zoomAnim.height = flick.height; 0132 zoomAnim.running = true; 0133 } else { 0134 flick.returnToBounds(); 0135 } 0136 } 0137 0138 ParallelAnimation { 0139 id: zoomAnim 0140 property real x: 0 0141 property real y: 0 0142 property real width: flick.width 0143 property real height: flick.height 0144 NumberAnimation { 0145 target: flick 0146 property: "contentWidth" 0147 from: flick.contentWidth 0148 to: zoomAnim.width 0149 duration: Maui.Style.units.longDuration 0150 easing.type: Easing.InOutQuad 0151 } 0152 NumberAnimation { 0153 target: flick 0154 property: "contentHeight" 0155 from: flick.contentHeight 0156 to: zoomAnim.height 0157 duration: Maui.Style.units.longDuration 0158 easing.type: Easing.InOutQuad 0159 } 0160 NumberAnimation { 0161 target: flick 0162 property: "contentY" 0163 from: flick.contentY 0164 to: zoomAnim.y 0165 duration: Maui.Style.units.longDuration 0166 easing.type: Easing.InOutQuad 0167 } 0168 NumberAnimation { 0169 target: flick 0170 property: "contentX" 0171 from: flick.contentX 0172 to: zoomAnim.x 0173 duration: Maui.Style.units.longDuration 0174 easing.type: Easing.InOutQuad 0175 } 0176 } 0177 0178 AnimatedImage 0179 { 0180 id: image 0181 width: flick.contentWidth 0182 height: flick.contentHeight 0183 fillMode: AnimatedImage.PreserveAspectFit 0184 autoTransform: true 0185 asynchronous: true 0186 onStatusChanged: 0187 { 0188 playing = (status == AnimatedImage.Ready) 0189 } 0190 0191 BusyIndicator 0192 { 0193 anchors.centerIn: parent 0194 running: parent.status === AnimatedImage.Loading 0195 } 0196 0197 MouseArea { 0198 anchors.fill: parent 0199 acceptedButtons: Qt.RightButton | Qt.LeftButton 0200 onClicked: (mouse) => 0201 { 0202 if(!Maui.Handy.isMobile && mouse.button === Qt.RightButton) 0203 { 0204 flick.rightClicked() 0205 } 0206 } 0207 0208 onPressAndHold: flick.pressAndHold() 0209 onDoubleClicked: (mouse) => 0210 { 0211 if (flick.interactive) { 0212 zoomAnim.x = 0; 0213 zoomAnim.y = 0; 0214 zoomAnim.width = flick.width; 0215 zoomAnim.height = flick.height; 0216 zoomAnim.running = true; 0217 flick.interactive = !flick.interactive 0218 } else { 0219 zoomAnim.x = mouse.x * 2; 0220 zoomAnim.y = mouse.y *2; 0221 zoomAnim.width = flick.width * 3; 0222 zoomAnim.height = flick.height * 3; 0223 zoomAnim.running = true; 0224 flick.interactive = !flick.interactive 0225 } 0226 } 0227 onWheel: (wheel) => 0228 { 0229 if (wheel.modifiers & Qt.ControlModifier) { 0230 if (wheel.angleDelta.y != 0) { 0231 var factor = 1 + wheel.angleDelta.y / 600; 0232 zoomAnim.running = false; 0233 0234 zoomAnim.width = Math.min(Math.max(flick.width, zoomAnim.width * factor), flick.width * 4); 0235 zoomAnim.height = Math.min(Math.max(flick.height, zoomAnim.height * factor), flick.height * 4); 0236 0237 //actual factors, may be less than factor 0238 var xFactor = zoomAnim.width / flick.contentWidth; 0239 var yFactor = zoomAnim.height / flick.contentHeight; 0240 0241 zoomAnim.x = flick.contentX * xFactor + (((wheel.x - flick.contentX) * xFactor) - (wheel.x - flick.contentX)) 0242 zoomAnim.y = flick.contentY * yFactor + (((wheel.y - flick.contentY) * yFactor) - (wheel.y - flick.contentY)) 0243 zoomAnim.running = true; 0244 0245 } else if (wheel.pixelDelta.y != 0) { 0246 flick.resizeContent(Math.min(Math.max(flick.width, flick.contentWidth + wheel.pixelDelta.y), flick.width * 4), 0247 Math.min(Math.max(flick.height, flick.contentHeight + wheel.pixelDelta.y), flick.height * 4), 0248 wheel); 0249 } 0250 } else { 0251 flick.contentX += wheel.pixelDelta.x; 0252 flick.contentY += wheel.pixelDelta.y; 0253 } 0254 } 0255 } 0256 } 0257 } 0258 0259 /** 0260 * @brief Forces the image to fit in the viewport. 0261 */ 0262 function fit() 0263 { 0264 image.width = image.sourceSize.width 0265 } 0266 0267 /** 0268 * @brief Forces the image to fill-in the viewport, this is done horizontally, so the image might be out of view vertically. 0269 */ 0270 function fill() 0271 { 0272 image.width = parent.width 0273 } 0274 0275 /** 0276 * @brief Forces the image to be rotated 90 degrees to the left. 0277 */ 0278 function rotateLeft() 0279 { 0280 image.rotation = image.rotation - 90 0281 } 0282 0283 /** 0284 * @brief Forces the image to be rotated 90 degrees to the right. 0285 */ 0286 function rotateRight() 0287 { 0288 image.rotation = image.rotation + 90 0289 } 0290 }