Unity 8
 All Classes Functions
ZoomableImage.qml
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  */
16 
17 import QtQuick 2.0
18 import Ubuntu.Components 0.1
19 import "Flickables" as Flickables
20 
21 /*! \brief Zoomable for image.
22 
23  This widget shows image contained in source,
24  can be zoomable accordingly with zoomable.
25  */
26 
27 Item {
28  id: root
29  property alias source: lazyImage.source
30  property var zoomable: false
31  property alias imageState: lazyImage.state
32  property alias scaleTo: lazyImage.scaleTo
33  property alias asynchronous: lazyImage.asynchronous
34 
35  Flickables.Flickable {
36  id: flickable
37  objectName: "flickable"
38  clip: true
39  contentHeight: imageContainer.height
40  contentWidth: imageContainer.width
41 
42  onHeightChanged: image.resetScale()
43  onWidthChanged: image.resetScale()
44  anchors.fill: parent
45 
46  Item {
47  id: imageContainer
48  objectName: "imageContainer"
49  width: Math.max(image.width * image.scale, flickable.width)
50  height: Math.max(image.height * image.scale, flickable.height)
51 
52  Item {
53  id: image
54  objectName: "image"
55  property alias imageState: lazyImage.state
56  property var prevScale
57  anchors.centerIn: parent
58 
59  signal imageReloaded
60 
61  LazyImage {
62  id: lazyImage
63  objectName: "lazyImage"
64  smooth: !flickable.movingVertically
65  anchors.fill: parent
66  fillMode: Image.PreserveAspectFit
67  scaleTo: "fit"
68 
69  onStateChanged: {
70  if (state == "ready") {
71  image.imageReloaded();
72  }
73  }
74  }
75 
76  onImageReloaded: {
77  image.height = lazyImage.sourceImage.implicitHeight
78  image.width = lazyImage.sourceImage.implicitWidth
79  image.resetScale();
80  }
81 
82  function resetScale() {
83  image.scale = Math.min(flickable.width / image.width, flickable.height / image.height);
84  pinchArea.minScale = image.scale;
85  prevScale = Math.min(image.scale, 1);
86  }
87 
88  onScaleChanged: {
89  var currentWidth = width * scale
90  var currentHeight = height * scale
91  var scaleRatio = scale / prevScale
92  if (currentWidth > flickable.width) {
93  var xpos = flickable.width / 2 + flickable.contentX;
94  var xoff = xpos * scaleRatio;
95  flickable.contentX = xoff - flickable.width / 2;
96  }
97  if (currentHeight > flickable.height) {
98  var ypos = flickable.height / 2 + flickable.contentY;
99  var yoff = ypos * scaleRatio;
100  flickable.contentY = yoff - flickable.height / 2;
101  }
102  prevScale = scale;
103  }
104  }
105  }
106 
107  PinchArea {
108  id: pinchArea
109  objectName: "pinchArea"
110  property real minScale: 1.0
111  anchors.fill: parent
112  enabled: zoomable ? zoomable : false
113 
114  pinch.target: image
115  pinch.minimumScale: minScale
116  pinch.maximumScale: 10
117 
118  onPinchFinished: flickable.returnToBounds()
119  }
120 
121  MouseArea {
122  id: mouseArea
123  objectName: "mouseArea"
124 
125  anchors.fill: parent
126  enabled: zoomable ? zoomable : false
127 
128  onWheel: {
129  var startScale = image.scale;
130  if (wheel.angleDelta.y > 0) {
131  image.scale = startScale + 0.1;
132  } else if (wheel.angleDelta.y < 0) {
133  if (image.scale > 0.1 && image.scale > pinchArea.minScale) {
134  image.scale = startScale - 0.1;
135  }
136  }
137  wheel.accepted = true;
138  }
139 
140  onPressed: {
141  mouse.accepted = false;
142  }
143 
144  onReleased: {
145  mouse.accepted = false;
146  }
147 
148  onClicked: {
149  mouse.accepted = false;
150  }
151  }
152  }
153 }