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 }