on a61d385 help sign in
unity8/qml/Components/ZoomableImage.qml
145 lines 4.9 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: image.source
30 property var zoomable: false
31 property alias state: image.state
32 property alias scale: image.scale
34 Flickable {
35 id: flickable
36 objectName: "flickable"
37 clip: true
38 contentHeight: imageContainer.height
39 contentWidth: imageContainer.width
40 onHeightChanged: image.calculateSize()
41 onWidthChanged: image.calculateSize()
42 anchors.fill: parent
44 Item {
45 id: imageContainer
46 objectName: "imageContainer"
47 width: Math.max(image.width * image.scale, flickable.width)
48 height: Math.max(image.height * image.scale, flickable.height)
50 LazyImage {
51 id: image
52 objectName: "image"
53 property real prevScale
54 smooth: !flickable.movingVertically
55 anchors.centerIn: parent
56 fillMode: Image.PreserveAspectFit
58 function calculateSize() {
59 image.scale = Math.min(flickable.width / image.width, flickable.height / image.height) * 0.98;
60 pinchArea.minScale = image.scale;
61 prevScale = Math.min(image.scale, 1);
62 }
64 onScaleChanged: {
65 if ((width * scale) > flickable.width) {
66 var xoff = (flickable.width / 2 + flickable.contentX) * scale / prevScale;
67 flickable.contentX = xoff - flickable.width / 2;
68 }
69 if ((height * scale) > flickable.height) {
70 var yoff = (flickable.height / 2 + flickable.contentY) * scale / prevScale;
71 flickable.contentY = yoff - flickable.height / 2;
72 }
74 prevScale = scale;
75 }
77 onStateChanged: {
78 if (state == "ready") {
79 image.width = flickable.width;
80 image.height = flickable.height;
81 calculateSize();
82 }
83 }
84 }
85 }
87 PinchArea {
88 id: pinchArea
89 objectName: "pinchArea"
90 property real minScale: 1.0
91 property real lastScale: 1.0
92 anchors.fill: parent
93 enabled: zoomable ? zoomable : false
95 pinch.target: image
96 pinch.minimumScale: minScale
98 onPinchFinished: flickable.returnToBounds()
99 }
101 MouseArea {
102 id: mouseArea
103 objectName: "mouseArea"
105 anchors.fill: parent
106 property bool doubleClicked: false
107 property bool swipeDone: false
108 property int startX
109 property int startY
110 property real startScale: pinchArea.minScale
111 enabled: zoomable ? zoomable : false
113 onWheel: {
114 if (wheel.angleDelta.y > 0) {
115 startScale = image.scale;
116 image.scale = startScale + 0.1;
117 } else if (wheel.angleDelta.y < 0) {
118 startScale = image.scale;
119 if (image.scale > 0.1) {
120 image.scale = startScale - 0.1;
121 }
122 }
123 wheel.accepted = true;
124 }
126 onPressed: {
127 startX = (mouse.x / image.scale);
128 startY = (mouse.y / image.scale);
129 startScale = image.scale;
130 }
132 onReleased: {
133 if (image.scale == startScale) {
134 var deltaX = (mouse.x / image.scale) - startX;
135 var deltaY = (mouse.y / image.scale) - startY;
137 if (image.scale == pinchArea.minScale &&
138 (Math.abs(deltaX) > 50 || Math.abs(deltaY) > 50)) {
139 swipeDone = true;
140 }
141 }
142 }
143 }
144 }