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 }