on xenial help sign in
Rewrite the zoomable image code.
b09b5b4Ying-Chun Liu
qml/Components/ZoomableImage.qml+50−44
@@ -26,10 +26,11 @@ import "../Components"
26
26
27Item {
27Item {
28 id: root
28 id: root
29− property alias source: image.source
29+ property alias source: lazyImage.source
30 property var zoomable: false
30 property var zoomable: false
31− property alias state: image.state
31+ property alias imageState: lazyImage.state
32− property alias scale: image.scale
32+ property alias scaleTo: lazyImage.scaleTo
33+ property alias asynchronous: lazyImage.asynchronous
33
34
34 Flickable {
35 Flickable {
35 id: flickable
36 id: flickable
@@ -37,8 +38,8 @@ Item {
37 clip: true
38 clip: true
38 contentHeight: imageContainer.height
39 contentHeight: imageContainer.height
39 contentWidth: imageContainer.width
40 contentWidth: imageContainer.width
40− onHeightChanged: image.calculateSize()
41+ onHeightChanged: image.resetScale()
41− onWidthChanged: image.calculateSize()
42+ onWidthChanged: image.resetScale()
42 anchors.fill: parent
43 anchors.fill: parent
43
44
44 Item {
45 Item {
@@ -47,40 +48,58 @@ Item {
47 width: Math.max(image.width * image.scale, flickable.width)
48 width: Math.max(image.width * image.scale, flickable.width)
48 height: Math.max(image.height * image.scale, flickable.height)
49 height: Math.max(image.height * image.scale, flickable.height)
49
50
50− LazyImage {
51+ Item {
51 id: image
52 id: image
52 objectName: "image"
53 objectName: "image"
53− property real prevScale
54+ property alias imageState: lazyImage.state
54− smooth: !flickable.movingVertically
55+ property var prevScale
55 anchors.centerIn: parent
56 anchors.centerIn: parent
56− fillMode: Image.PreserveAspectFit
57
57
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+
58− function calculateSize() {
81+ function resetScale() {
59− image.scale = Math.min(flickable.width / image.width, flickable.height / image.height) * 0.98;
82+ image.scale = Math.min(flickable.width / image.width, flickable.height / image.height);
60 pinchArea.minScale = image.scale;
83 pinchArea.minScale = image.scale;
61 prevScale = Math.min(image.scale, 1);
84 prevScale = Math.min(image.scale, 1);
62 }
85 }
63
86
64 onScaleChanged: {
87 onScaleChanged: {
88+ var currentWidth = width * scale
89+ var currentHeight = height * scale
90+ var scaleRatio = scale / prevScale
65− if ((width * scale) > flickable.width) {
91+ if (currentWidth > flickable.width) {
66− var xoff = (flickable.width / 2 + flickable.contentX) * scale / prevScale;
92+ var xpos = flickable.width / 2 + flickable.contentX;
93+ var xoff = xpos * scaleRatio;
67 flickable.contentX = xoff - flickable.width / 2;
94 flickable.contentX = xoff - flickable.width / 2;
68 }
95 }
69− if ((height * scale) > flickable.height) {
96+ if (currentHeight > flickable.height) {
70− var yoff = (flickable.height / 2 + flickable.contentY) * scale / prevScale;
97+ var ypos = flickable.height / 2 + flickable.contentY;
98+ var yoff = ypos * scaleRatio;
71 flickable.contentY = yoff - flickable.height / 2;
99 flickable.contentY = yoff - flickable.height / 2;
72 }
100 }
73−
74 prevScale = scale;
101 prevScale = scale;
75 }
102 }
76−
77− onStateChanged: {
78− if (state == "ready") {
79− image.width = flickable.width;
80− image.height = flickable.height;
81− calculateSize();
82− }
83− }
84 }
103 }
85 }
104 }
86
105
@@ -88,7 +107,6 @@ Item {
88 id: pinchArea
107 id: pinchArea
89 objectName: "pinchArea"
108 objectName: "pinchArea"
90 property real minScale: 1.0
109 property real minScale: 1.0
91− property real lastScale: 1.0
92 anchors.fill: parent
110 anchors.fill: parent
93 enabled: zoomable ? zoomable : false
111 enabled: zoomable ? zoomable : false
94
112
@@ -103,20 +121,14 @@ Item {
103 objectName: "mouseArea"
121 objectName: "mouseArea"
104
122
105 anchors.fill: parent
123 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
124 enabled: zoomable ? zoomable : false
112
125
113 onWheel: {
126 onWheel: {
127+ var startScale = image.scale;
114 if (wheel.angleDelta.y > 0) {
128 if (wheel.angleDelta.y > 0) {
115− startScale = image.scale;
116 image.scale = startScale + 0.1;
129 image.scale = startScale + 0.1;
117 } else if (wheel.angleDelta.y < 0) {
130 } 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) {
120 image.scale = startScale - 0.1;
132 image.scale = startScale - 0.1;
121 }
133 }
122 }
134 }
@@ -124,21 +136,15 @@ Item {
124 }
136 }
125
137
126 onPressed: {
138 onPressed: {
127− startX = (mouse.x / image.scale);
139+ mouse.accepted = false;
128− startY = (mouse.y / image.scale);
129− startScale = image.scale;
130 }
140 }
131
141
132 onReleased: {
142 onReleased: {
133− if (image.scale == startScale) {
143+ mouse.accepted = false;
134− var deltaX = (mouse.x / image.scale) - startX;
144+ }
135− var deltaY = (mouse.y / image.scale) - startY;
136
145
137− if (image.scale == pinchArea.minScale &&
146+ onClicked: {
138− (Math.abs(deltaX) > 50 || Math.abs(deltaY) > 50)) {
147+ mouse.accepted = false;
139− swipeDone = true;
140− }
141− }
142 }
148 }
143 }
149 }
144 }
150 }
tests/qmltests/Components/tst_ZoomableImage.qml+17−6
@@ -44,6 +44,7 @@ Rectangle {
44 id: zoomableImage
44 id: zoomableImage
45 width: parent.width
45 width: parent.width
46 anchors.fill: parent
46 anchors.fill: parent
47+ asynchronous: false
47 }
48 }
48
49
49 SignalSpy {
50 SignalSpy {
@@ -55,22 +56,24 @@ Rectangle {
55 when: windowShown
56 when: windowShown
56
57
57 function test_loadImage() {
58 function test_loadImage() {
58− var image = findChild(zoomableImage, "image");
59+ var lazyImage = findChild(zoomableImage, "lazyImage");
59
60
60 zoomableImage.source = widgetData0["source"];
61 zoomableImage.source = widgetData0["source"];
61 zoomableImage.zoomable = widgetData0["zoomable"];
62 zoomableImage.zoomable = widgetData0["zoomable"];
62 waitForRendering(zoomableImage);
63 waitForRendering(zoomableImage);
63− tryCompare(image, "state", "default");
64+ tryCompare(zoomableImage, "imageState", "default");
64
65
65 signalSpy.signalName = "onStateChanged";
66 signalSpy.signalName = "onStateChanged";
66− signalSpy.target = image;
67+ signalSpy.target = lazyImage;
67 signalSpy.clear();
68 signalSpy.clear();
68
69
69 zoomableImage.source = widgetData1["source"];
70 zoomableImage.source = widgetData1["source"];
70 zoomableImage.zoomable = widgetData1["zoomable"];
71 zoomableImage.zoomable = widgetData1["zoomable"];
72+ waitForRendering(lazyImage);
73+ tryCompareFunction(function() { return get_filename(lazyImage.source.toString()) === get_filename(widgetData1["source"]); }, true);
71 waitForRendering(zoomableImage);
74 waitForRendering(zoomableImage);
72− tryCompare(image, "state", "ready");
75+ tryCompare(zoomableImage, "imageState", "ready");
73− compare (signalSpy.count, 2);
76+ compare (signalSpy.count, 1);
74 }
77 }
75
78
76 function test_zoomable_data() {
79 function test_zoomable_data() {
@@ -87,8 +90,15 @@ Rectangle {
87 ]
90 ]
88 }
91 }
89
92
93+ function get_filename(a) {
94+ var wordsA = a.split("/");
95+ var filenameA = wordsA[wordsA.length-1];
96+ return filenameA;
97+ }
98+
90 function test_zoomable(data) {
99 function test_zoomable(data) {
91 var image = findChild(zoomableImage, "image");
100 var image = findChild(zoomableImage, "image");
101+ var lazyImage = findChild(zoomableImage, "lazyImage");
92
102
93 signalSpy.signalName = "onScaleChanged";
103 signalSpy.signalName = "onScaleChanged";
94 signalSpy.target = image;
104 signalSpy.target = image;
@@ -98,7 +108,8 @@ Rectangle {
98 zoomableImage.zoomable = data.zoomable;
108 zoomableImage.zoomable = data.zoomable;
99 waitForRendering(zoomableImage);
109 waitForRendering(zoomableImage);
100
110
101− tryCompare(image, "state", "ready");
111+ tryCompare(zoomableImage, "imageState", "ready");
112+ tryCompareFunction(function() { return get_filename(lazyImage.source.toString()) === get_filename(data.source); }, true);
102 waitForRendering(image);
113 waitForRendering(image);
103
114
104 var event1 = touchEvent();
115 var event1 = touchEvent();