on xenial help sign in
Rewrite the zoomable image code.
commitb09b5b41d3200d5091ac9edcbf285d2bc622c4b1 authorYing-Chun Liu <paul.liu@canonical.com> dateMon 24 Mar 2014, 23:26 (13 years ago) parenta61d385
2 files changed+67−50
qml/Components/ZoomableImage.qml+50−44 tests/qmltests/Components/tst_ZoomableImage.qml+17−6
qml/Components/ZoomableImage.qml+50−44
@@ -26,10 +26,11 @@ import "../Components"
2626
2727Item {
2828 id: root
29− property alias source: image.source
29+ property alias source: lazyImage.source
3030 property var zoomable: false
31− property alias state: image.state
32− property alias scale: image.scale
31+ property alias imageState: lazyImage.state
32+ property alias scaleTo: lazyImage.scaleTo
33+ property alias asynchronous: lazyImage.asynchronous
3334
3435 Flickable {
3536 id: flickable
@@ -37,8 +38,8 @@ Item {
3738 clip: true
3839 contentHeight: imageContainer.height
3940 contentWidth: imageContainer.width
40− onHeightChanged: image.calculateSize()
41− onWidthChanged: image.calculateSize()
41+ onHeightChanged: image.resetScale()
42+ onWidthChanged: image.resetScale()
4243 anchors.fill: parent
4344
4445 Item {
@@ -47,40 +48,58 @@ Item {
4748 width: Math.max(image.width * image.scale, flickable.width)
4849 height: Math.max(image.height * image.scale, flickable.height)
4950
50− LazyImage {
51+ Item {
5152 id: image
5253 objectName: "image"
53− property real prevScale
54− smooth: !flickable.movingVertically
54+ property alias imageState: lazyImage.state
55+ property var prevScale
5556 anchors.centerIn: parent
56− fillMode: Image.PreserveAspectFit
5757
58− function calculateSize() {
59− image.scale = Math.min(flickable.width / image.width, flickable.height / image.height) * 0.98;
58+ signal imageReloaded
59+
60+ LazyImage {
61+ id: lazyImage
62+ objectName: "lazyImage"
63+ smooth: !flickable.movingVertically
64+ anchors.fill: parent
65+ fillMode: Image.PreserveAspectFit
66+ scaleTo: "fit"
67+
68+ onStateChanged: {
69+ if (state == "ready") {
70+ image.imageReloaded();
71+ }
72+ }
73+ }
74+
75+ onImageReloaded: {
76+ image.width = flickable.width;
77+ image.height = flickable.height;
78+ image.resetScale();
79+ }
80+
81+ function resetScale() {
82+ image.scale = Math.min(flickable.width / image.width, flickable.height / image.height);
6083 pinchArea.minScale = image.scale;
6184 prevScale = Math.min(image.scale, 1);
6285 }
6386
6487 onScaleChanged: {
65− if ((width * scale) > flickable.width) {
66− var xoff = (flickable.width / 2 + flickable.contentX) * scale / prevScale;
88+ var currentWidth = width * scale
89+ var currentHeight = height * scale
90+ var scaleRatio = scale / prevScale
91+ if (currentWidth > flickable.width) {
92+ var xpos = flickable.width / 2 + flickable.contentX;
93+ var xoff = xpos * scaleRatio;
6794 flickable.contentX = xoff - flickable.width / 2;
6895 }
69− if ((height * scale) > flickable.height) {
70− var yoff = (flickable.height / 2 + flickable.contentY) * scale / prevScale;
96+ if (currentHeight > flickable.height) {
97+ var ypos = flickable.height / 2 + flickable.contentY;
98+ var yoff = ypos * scaleRatio;
7199 flickable.contentY = yoff - flickable.height / 2;
72100 }
73−
74101 prevScale = scale;
75102 }
76−
77− onStateChanged: {
78− if (state == "ready") {
79− image.width = flickable.width;
80− image.height = flickable.height;
81− calculateSize();
82− }
83− }
84103 }
85104 }
86105
@@ -88,7 +107,6 @@ Item {
88107 id: pinchArea
89108 objectName: "pinchArea"
90109 property real minScale: 1.0
91− property real lastScale: 1.0
92110 anchors.fill: parent
93111 enabled: zoomable ? zoomable : false
94112
@@ -103,20 +121,14 @@ Item {
103121 objectName: "mouseArea"
104122
105123 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
111124 enabled: zoomable ? zoomable : false
112125
113126 onWheel: {
127+ var startScale = image.scale;
114128 if (wheel.angleDelta.y > 0) {
115− startScale = image.scale;
116129 image.scale = startScale + 0.1;
117130 } else if (wheel.angleDelta.y < 0) {
118− startScale = image.scale;
119− if (image.scale > 0.1) {
131+ if (image.scale > 0.1 && image.scale > pinchArea.minScale) {
120132 image.scale = startScale - 0.1;
121133 }
122134 }
@@ -124,21 +136,15 @@ Item {
124136 }
125137
126138 onPressed: {
127− startX = (mouse.x / image.scale);
128− startY = (mouse.y / image.scale);
129− startScale = image.scale;
139+ mouse.accepted = false;
130140 }
131141
132142 onReleased: {
133− if (image.scale == startScale) {
134− var deltaX = (mouse.x / image.scale) - startX;
135− var deltaY = (mouse.y / image.scale) - startY;
143+ mouse.accepted = false;
144+ }
136145
137− if (image.scale == pinchArea.minScale &&
138− (Math.abs(deltaX) > 50 || Math.abs(deltaY) > 50)) {
139− swipeDone = true;
140− }
141− }
146+ onClicked: {
147+ mouse.accepted = false;
142148 }
143149 }
144150 }
tests/qmltests/Components/tst_ZoomableImage.qml+17−6
@@ -44,6 +44,7 @@ Rectangle {
4444 id: zoomableImage
4545 width: parent.width
4646 anchors.fill: parent
47+ asynchronous: false
4748 }
4849
4950 SignalSpy {
@@ -55,22 +56,24 @@ Rectangle {
5556 when: windowShown
5657
5758 function test_loadImage() {
58− var image = findChild(zoomableImage, "image");
59+ var lazyImage = findChild(zoomableImage, "lazyImage");
5960
6061 zoomableImage.source = widgetData0["source"];
6162 zoomableImage.zoomable = widgetData0["zoomable"];
6263 waitForRendering(zoomableImage);
63− tryCompare(image, "state", "default");
64+ tryCompare(zoomableImage, "imageState", "default");
6465
6566 signalSpy.signalName = "onStateChanged";
66− signalSpy.target = image;
67+ signalSpy.target = lazyImage;
6768 signalSpy.clear();
6869
6970 zoomableImage.source = widgetData1["source"];
7071 zoomableImage.zoomable = widgetData1["zoomable"];
72+ waitForRendering(lazyImage);
73+ tryCompareFunction(function() { return get_filename(lazyImage.source.toString()) === get_filename(widgetData1["source"]); }, true);
7174 waitForRendering(zoomableImage);
72− tryCompare(image, "state", "ready");
73− compare (signalSpy.count, 2);
75+ tryCompare(zoomableImage, "imageState", "ready");
76+ compare (signalSpy.count, 1);
7477 }
7578
7679 function test_zoomable_data() {
@@ -87,8 +90,15 @@ Rectangle {
8790 ]
8891 }
8992
93+ function get_filename(a) {
94+ var wordsA = a.split("/");
95+ var filenameA = wordsA[wordsA.length-1];
96+ return filenameA;
97+ }
98+
9099 function test_zoomable(data) {
91100 var image = findChild(zoomableImage, "image");
101+ var lazyImage = findChild(zoomableImage, "lazyImage");
92102
93103 signalSpy.signalName = "onScaleChanged";
94104 signalSpy.target = image;
@@ -98,7 +108,8 @@ Rectangle {
98108 zoomableImage.zoomable = data.zoomable;
99109 waitForRendering(zoomableImage);
100110
101− tryCompare(image, "state", "ready");
111+ tryCompare(zoomableImage, "imageState", "ready");
112+ tryCompareFunction(function() { return get_filename(lazyImage.source.toString()) === get_filename(data.source); }, true);
102113 waitForRendering(image);
103114
104115 var event1 = touchEvent();