Warning, /maui/mauikit/src/controls.6/ImageViewer.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  * @inherit QtQuick.Flickable
0015  * @brief A view container for displaying images.
0016  * 
0017  * <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>
0018  * 
0019  *  This control along with the AnimatedImageViewer are meant to display images, with support for zooming in and out with touch or mouse gestures, and keyboard shortcuts.  
0020  *  
0021  */
0022 Flickable
0023 {
0024     id: flick
0025     
0026     contentWidth: width
0027     contentHeight: height
0028     boundsBehavior: Flickable.StopAtBounds
0029     boundsMovement: Flickable.StopAtBounds
0030     interactive: contentWidth > width || contentHeight > height
0031     clip: false
0032     
0033     ScrollBar.vertical: ScrollBar
0034     {
0035         visible: false
0036     }
0037     
0038     ScrollBar.horizontal: ScrollBar
0039     {
0040         visible: false
0041     }
0042 
0043     /**
0044      * @brief This an alias to the actual control painting the image. 
0045      * This control is handled by a QQC2 Image.
0046      * @note See Qt documentation for more information about the Image control.
0047      * @property Image ImageViewer::image
0048      */
0049     property alias image: image
0050     
0051     /**
0052      * @brief The painted size of the image.
0053      * As taken from Qt documentation: This property holds the scaled width and height of the full-frame image.
0054      * 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. 
0055      * @property size ImageViewer::sourceSize
0056      */
0057     property alias sourceSize : image.sourceSize
0058     
0059      /**
0060      * @brief The fill mode of the image. The possible values can be found on the Image control documentation from Qt.
0061      * By default this is set to `Image.PreserveAspectFit`.
0062      * @property enumaration ImageViewer::fillMode
0063      */
0064     property alias fillMode: image.fillMode
0065     
0066     /**
0067      * @brief Whether the image should be loaded asynchronously. 
0068      * By default this is set to `true`.
0069      * @property bool ImageViewer::asynchronous
0070      */
0071     property alias asynchronous : image.asynchronous
0072     
0073     /**
0074      * @brief If the image should be cached in memory. 
0075      * The default value is set to `true`
0076      * @property bool ImageViewer::cache
0077      */
0078     property alias cache: image.cache
0079     
0080      /**
0081      * @brief The painted width of the image. This the same as using the image `sourceSize` property to set the width.
0082      * @property int ImageViewer::imageWidth
0083      */
0084     property alias imageWidth: image.sourceSize.width
0085     
0086     /**
0087      * @brief The painted height of the image. This the same as using the image `sourceSize` property to set the height.
0088      * @property int ImageViewer::imageHeight
0089      */
0090     property alias imageHeight: image.sourceSize.height
0091     
0092     /**
0093      * @brief The source of the image. Can be a remote or local file URL.
0094      * @property url ImageViewer::source
0095      */
0096     property alias source : image.source
0097     
0098     /**
0099      * @brief Emitted when the image area has been right clicked with a mouse event.
0100      */
0101     signal rightClicked()
0102     
0103     /**
0104      * @brief Emitted when the image area has been pressed for a few seconds. 
0105      */
0106     signal pressAndHold()
0107     
0108     PinchArea
0109     {
0110         width: Math.max(flick.contentWidth, flick.width)
0111         height: Math.max(flick.contentHeight, flick.height)
0112         
0113         property real initialWidth
0114         property real initialHeight
0115         
0116         onPinchStarted: {
0117             initialWidth = flick.contentWidth
0118             initialHeight = flick.contentHeight
0119         }
0120         
0121         onPinchUpdated: {
0122             // adjust content pos due to drag
0123             flick.contentX += pinch.previousCenter.x - pinch.center.x
0124             flick.contentY += pinch.previousCenter.y - pinch.center.y
0125             
0126             // resize content
0127             flick.resizeContent(Math.max(flick.width*0.7, initialWidth * pinch.scale), Math.max(flick.height*0.7, initialHeight * pinch.scale), pinch.center)
0128         }
0129         
0130         onPinchFinished: {
0131             // Move its content within bounds.
0132             if (flick.contentWidth < flick.width ||
0133                     flick.contentHeight < flick.height) {
0134                 zoomAnim.x = 0;
0135                 zoomAnim.y = 0;
0136                 zoomAnim.width = flick.width;
0137                 zoomAnim.height = flick.height;
0138                 zoomAnim.running = true;
0139             } else {
0140                 flick.returnToBounds();
0141             }
0142         }
0143         
0144         ParallelAnimation {
0145             id: zoomAnim
0146             property real x: 0
0147             property real y: 0
0148             property real width: flick.width
0149             property real height: flick.height
0150             NumberAnimation {
0151                 target: flick
0152                 property: "contentWidth"
0153                 from: flick.contentWidth
0154                 to: zoomAnim.width
0155                 duration: Maui.Style.units.longDuration
0156                 easing.type: Easing.InOutQuad
0157             }
0158             NumberAnimation {
0159                 target: flick
0160                 property: "contentHeight"
0161                 from: flick.contentHeight
0162                 to: zoomAnim.height
0163                 duration: Maui.Style.units.longDuration
0164                 easing.type: Easing.InOutQuad
0165             }
0166             NumberAnimation {
0167                 target: flick
0168                 property: "contentY"
0169                 from: flick.contentY
0170                 to: zoomAnim.y
0171                 duration: Maui.Style.units.longDuration
0172                 easing.type: Easing.InOutQuad
0173             }
0174             NumberAnimation {
0175                 target: flick
0176                 property: "contentX"
0177                 from: flick.contentX
0178                 to: zoomAnim.x
0179                 duration: Maui.Style.units.longDuration
0180                 easing.type: Easing.InOutQuad
0181             }
0182         }
0183         
0184         Image
0185         {
0186             id: image
0187             width: flick.contentWidth
0188             height: flick.contentHeight
0189             fillMode: Image.PreserveAspectFit
0190             autoTransform: true
0191             asynchronous: true
0192             
0193             Maui.ProgressIndicator
0194             {
0195                 width: parent.width
0196                 anchors.bottom: parent.bottom
0197                 visible: image.status === Image.Loading
0198             }
0199             
0200             Maui.Holder
0201             {
0202                 anchors.fill: parent
0203                 visible: image.status === Image.Error || image.status === Image.Null
0204                 title: i18nd("mauikit", "Oops!")
0205                 body: i18nd("mauikit", "The image could not be loaded.")
0206                 emoji: "qrc:/assets/dialog-information.svg"
0207             }
0208             
0209             MouseArea {
0210                 anchors.fill: parent
0211                 acceptedButtons:  Qt.RightButton | Qt.LeftButton
0212                 onClicked: (mouse) =>
0213                            {
0214                                if(!Maui.Handy.isMobile && mouse.button === Qt.RightButton)
0215                                {
0216                                    flick.rightClicked()
0217                                }
0218                            }
0219                 
0220                 onPressAndHold: flick.pressAndHold()
0221 
0222                 onDoubleClicked: (mouse) =>
0223                                  {
0224                                      if (flick.interactive)
0225                                      {
0226                                          zoomAnim.x = 0;
0227                                          zoomAnim.y = 0;
0228                                          zoomAnim.width = flick.width;
0229                                          zoomAnim.height = flick.height;
0230                                          zoomAnim.running = true;
0231                                          flick.interactive = !flick.interactive
0232                                      } else
0233                                      {
0234                                          zoomAnim.x = mouse.x * 2;
0235                                          zoomAnim.y = mouse.y *2;
0236                                          zoomAnim.width = flick.width * 3;
0237                                          zoomAnim.height = flick.height * 3;
0238                                          zoomAnim.running = true;
0239                                          flick.interactive = !flick.interactive
0240                                      }
0241                                  }
0242 
0243                 onWheel: (wheel) =>
0244                          {
0245                              if (wheel.modifiers & Qt.ControlModifier) {
0246                                  if (wheel.angleDelta.y != 0) {
0247                                      var factor = 1 + wheel.angleDelta.y / 600;
0248                                      zoomAnim.running = false;
0249 
0250                                      zoomAnim.width = Math.min(Math.max(flick.width, zoomAnim.width * factor), flick.width * 4);
0251                                      zoomAnim.height = Math.min(Math.max(flick.height, zoomAnim.height * factor), flick.height * 4);
0252 
0253                                      //actual factors, may be less than factor
0254                                      var xFactor = zoomAnim.width / flick.contentWidth;
0255                                      var yFactor = zoomAnim.height / flick.contentHeight;
0256 
0257                                      zoomAnim.x = flick.contentX * xFactor + (((wheel.x - flick.contentX) * xFactor) - (wheel.x - flick.contentX))
0258                                      zoomAnim.y = flick.contentY * yFactor + (((wheel.y - flick.contentY) * yFactor) - (wheel.y - flick.contentY))
0259                                      zoomAnim.running = true;
0260 
0261                                  } else if (wheel.pixelDelta.y != 0) {
0262                                      flick.resizeContent(Math.min(Math.max(flick.width, flick.contentWidth + wheel.pixelDelta.y), flick.width * 4),
0263                                                          Math.min(Math.max(flick.height, flick.contentHeight + wheel.pixelDelta.y), flick.height * 4),
0264                                                          wheel);
0265                                  }
0266                              } else {
0267 
0268                                  if(zoomAnim.width !== flick.contentWidth || zoomAnim.height !== flick.contentHeight)
0269                                  {
0270                                      flick.contentX += wheel.pixelDelta.x;
0271                                      flick.contentY += wheel.pixelDelta.y;
0272                                  }else
0273                                  {
0274                                      wheel.accepted = false
0275                                  }
0276                              }
0277                          }
0278             }
0279         }
0280     }
0281     
0282     /**
0283      * @brief Forces the image to fit in the viewport.
0284      */
0285     function fit()
0286     {
0287         image.width = image.sourceSize.width
0288     }
0289     
0290     /**
0291      * @brief Forces the image to fill-in the viewport, this is done horizontally, so the image might be out of view vertically.
0292      */
0293     function fill()
0294     {
0295         image.width = parent.width
0296     }
0297     
0298     /**
0299      * @brief Forces the image to be rotated 90 degrees to the left.
0300      */
0301     function rotateLeft()
0302     {
0303         image.rotation = image.rotation - 90
0304     }
0305     
0306     /**
0307      * @brief Forces the image to be rotated 90 degrees to the right.
0308      */
0309     function rotateRight()
0310     {
0311         image.rotation = image.rotation + 90
0312     }
0313 }