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 }