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 }