on 436b607 help sign in
unity8/qml/Components/ZoomableImage.qml
156 lines 5.3 kB history raw
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17import QtQuick 2.0
18import Ubuntu.Components 0.1
19import "../Components"
21/*! \brief Zoomable for image.
23 This widget shows image contained in source,
24 can be zoomable accordingly with zoomable.
25 */
27Item {
28 id: root
29 property alias source: imageRenderer.source
30 property var zoomable: false
31 property alias imageStatus: imageRenderer.status
32 property alias asynchronous: imageRenderer.asynchronous
34 Flickable {
35 id: flickable
36 objectName: "flickable"
37 clip: true // FIXME maybe we can remove this, or just not clip in few cases
38 contentHeight: imageContainer.height
39 contentWidth: imageContainer.width
41 onHeightChanged: image.resetScale()
42 onWidthChanged: image.resetScale()
43 anchors.fill: parent
45 Item {
46 id: imageContainer
47 objectName: "imageContainer"
48 width: Math.max(image.width * image.scale, flickable.width)
49 height: Math.max(image.height * image.scale, flickable.height)
51 Item {
52 id: image
53 objectName: "image"
54 property alias imageStatus: imageRenderer.status
55 property var prevScale
56 anchors.centerIn: parent
58 signal imageReloaded
60 Image {
61 id: imageRenderer
62 objectName: "imageRenderer"
63 smooth: !flickable.movingVertically
64 anchors.fill: parent
65 fillMode: Image.PreserveAspectFit
67 readonly property int sourceSizeMultiplier: 3
69 sourceSize.width: root.width * sourceSizeMultiplier <= root.height * sourceSizeMultiplier ? root.width * sourceSizeMultiplier : 0
70 sourceSize.height: root.height * sourceSizeMultiplier <= root.width * sourceSizeMultiplier ? root.height * sourceSizeMultiplier : 0
72 onStatusChanged: {
73 if (status === Image.Ready) {
74 image.imageReloaded();
75 }
76 }
77 }
79 onImageReloaded: {
80 image.height = imageRenderer.implicitHeight
81 image.width = imageRenderer.implicitWidth
82 image.resetScale();
83 }
85 function resetScale() {
86 image.scale = Math.min(flickable.width / image.width, flickable.height / image.height);
87 pinchArea.minScale = image.scale;
88 prevScale = Math.min(image.scale, 1);
89 }
91 onScaleChanged: {
92 var currentWidth = width * scale
93 var currentHeight = height * scale
94 var scaleRatio = scale / prevScale
95 if (currentWidth > flickable.width) {
96 var xpos = flickable.width / 2 + flickable.contentX;
97 var xoff = xpos * scaleRatio;
98 flickable.contentX = xoff - flickable.width / 2;
99 }
100 if (currentHeight > flickable.height) {
101 var ypos = flickable.height / 2 + flickable.contentY;
102 var yoff = ypos * scaleRatio;
103 flickable.contentY = yoff - flickable.height / 2;
104 }
105 prevScale = scale;
106 }
107 }
108 }
110 MouseArea {
111 id: mouseArea
112 objectName: "mouseArea"
114 anchors.fill: parent
115 enabled: zoomable ? zoomable : false
117 onWheel: {
118 var startScale = image.scale;
119 if (wheel.angleDelta.y > 0) {
120 image.scale = startScale + 0.1;
121 } else if (wheel.angleDelta.y < 0) {
122 if (image.scale > 0.1 && image.scale > pinchArea.minScale) {
123 image.scale = startScale - 0.1;
124 }
125 }
126 wheel.accepted = true;
127 }
129 onPressed: {
130 mouse.accepted = false;
131 }
133 onReleased: {
134 mouse.accepted = false;
135 }
137 onClicked: {
138 mouse.accepted = false;
139 }
140 }
141 }
143 PinchArea {
144 id: pinchArea
145 objectName: "pinchArea"
146 property real minScale: 1.0
147 anchors.fill: flickable
148 enabled: zoomable ? zoomable : false
150 pinch.target: image
151 pinch.minimumScale: minScale
152 pinch.maximumScale: 10
154 onPinchFinished: flickable.returnToBounds()
155 }