on xenial help sign in
Fix for black preview zoomable images, use fullscreen preview
e0142d1Andrea Cimitan
qml/Components/ZoomableImage.qml+17−14
@@ -26,16 +26,15 @@ import "Flickables" as Flickables
26
26
27Item {
27Item {
28 id: root
28 id: root
29− property alias source: lazyImage.source
29+ property alias source: imageRenderer.source
30 property var zoomable: false
30 property var zoomable: false
31− property alias imageState: lazyImage.state
31+ property alias imageStatus: imageRenderer.status
32− property alias scaleTo: lazyImage.scaleTo
33− property alias asynchronous: lazyImage.asynchronous
32+ property alias asynchronous: imageRenderer.asynchronous
34
33
35 Flickables.Flickable {
34 Flickables.Flickable {
36 id: flickable
35 id: flickable
37 objectName: "flickable"
36 objectName: "flickable"
38− clip: true
37+ clip: true // FIXME maybe we can remove this, or just not clip in few cases
39 contentHeight: imageContainer.height
38 contentHeight: imageContainer.height
40 contentWidth: imageContainer.width
39 contentWidth: imageContainer.width
41
40
@@ -52,30 +51,34 @@ Item {
52 Item {
51 Item {
53 id: image
52 id: image
54 objectName: "image"
53 objectName: "image"
55− property alias imageState: lazyImage.state
54+ property alias imageStatus: imageRenderer.status
56 property var prevScale
55 property var prevScale
57 anchors.centerIn: parent
56 anchors.centerIn: parent
58
57
59 signal imageReloaded
58 signal imageReloaded
60
59
61− LazyImage {
60+ Image {
62− id: lazyImage
61+ id: imageRenderer
63− objectName: "lazyImage"
62+ objectName: "imageRenderer"
64 smooth: !flickable.movingVertically
63 smooth: !flickable.movingVertically
65 anchors.fill: parent
64 anchors.fill: parent
66 fillMode: Image.PreserveAspectFit
65 fillMode: Image.PreserveAspectFit
67− scaleTo: "fit"
68
66
69− onStateChanged: {
67+ readonly property int sourceSizeMultiplier: 3
70− if (state == "ready") {
68+
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
71+
72+ onStatusChanged: {
73+ if (status === Image.Ready) {
71 image.imageReloaded();
74 image.imageReloaded();
72 }
75 }
73 }
76 }
74 }
77 }
75
78
76 onImageReloaded: {
79 onImageReloaded: {
77− image.height = lazyImage.sourceImage.implicitHeight
80+ image.height = imageRenderer.implicitHeight
78− image.width = lazyImage.sourceImage.implicitWidth
81+ image.width = imageRenderer.implicitWidth
79 image.resetScale();
82 image.resetScale();
80 }
83 }
81
84
qml/Dash/Previews/PreviewZoomableImage.qml+103−5
@@ -28,11 +28,109 @@ PreviewWidget {
28 id: root
28 id: root
29 implicitHeight: units.gu(22)
29 implicitHeight: units.gu(22)
30
30
31− ZoomableImage {
31+ property Item rootItem: QuickUtils.rootItem(root)
32− id: image
32+
33+ LazyImage {
34+ id: lazyImage
33− objectName: "image"
35+ objectName: "lazyImage"
36+ anchors {
37+ top: parent.top
38+ bottom: parent.bottom
39+ }
40+ scaleTo: "height"
34 source: widgetData["source"]
41 source: widgetData["source"]
42+
43+ borderSource: mouseArea.pressed ? "radius_pressed.sci" : "radius_idle.sci"
44+
45+ MouseArea {
46+ id: mouseArea
47+ anchors.fill: parent
48+ onClicked: {
49+ zoomableImageContainer.initialX = rootItem.mapFromItem(parent, 0, 0).x
50+ zoomableImageContainer.initialY = rootItem.mapFromItem(parent, 0, 0).y
51+ zoomableImageContainer.visible = true;
52+ }
53+ }
54+ }
55+
56+ Rectangle {
57+ id: zoomableImageContainer
58+ objectName: "zoomableImageContainer"
59+
60+ readonly property real initialScale: lazyImage.height / rootItem.height
61+ readonly property real scaleProgress: (scale - initialScale) / (1.0 - initialScale)
62+ property real initialX: 0
63+ property real initialY: 0
64+
65+ parent: rootItem
66+ width: parent.width
67+ height: parent.height
68+ visible: false
69+ clip: visible && scale < 1.0
70+ scale: visible ? 1.0 : initialScale
71+ transformOrigin: Item.TopLeft
72+ transform: Translate {
73+ x: zoomableImageContainer.initialX - zoomableImageContainer.initialX * zoomableImageContainer.scaleProgress
74+ y: zoomableImageContainer.initialY - zoomableImageContainer.initialY * zoomableImageContainer.scaleProgress
75+ }
76+ color: "black"
77+ radius: units.gu(1) - units.gu(1) * zoomableImageContainer.scaleProgress
78+
79+ Behavior on scale {
80+ enabled: !zoomableImageContainer.visible
81+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
82+ }
83+
84+ ZoomableImage {
85+ id: screenshot
86+ anchors.fill: parent
87+ source: widgetData["source"]
35− zoomable: widgetData["zoomable"] ? widgetData["zoomable"] : false
88+ zoomable: widgetData["zoomable"] ? widgetData["zoomable"] : false
89+ }
90+
91+ Rectangle {
92+ id: zoomableImageHeader
93+
94+ anchors {
95+ left: parent.left
96+ right: parent.right
97+ }
98+ height: units.gu(7)
99+ visible: opacity > 0
100+ opacity: zoomableImageContainer.scaleProgress > 0.6 ? 0.8 : 0
101+ color: "black"
102+
103+ Behavior on opacity {
104+ UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration }
105+ }
106+
107+ AbstractButton {
108+ id: zoomableImageCloseButton
109+ objectName: "zoomableImageCloseButton"
110+ anchors {
111+ top: parent.top
112+ bottom: parent.bottom
113+ }
114+ width: units.gu(8)
115+ height: width
116+
117+ onClicked: zoomableImageContainer.visible = false
118+
119+ Rectangle {
36− anchors.fill: parent
120+ anchors.fill: parent
121+ color: Qt.rgba(1.0, 1.0, 1.0, 0.3)
122+ visible: zoomableImageCloseButton.pressed
123+ }
124+
125+ Icon {
126+ id: icon
127+ anchors.centerIn: parent
128+ width: units.gu(2.5)
129+ height: width
130+ color: Theme.palette.normal.foregroundText
131+ name: "close"
132+ }
133+ }
134+ }
37 }
135 }
38}
136}
tests/qmltests/Components/tst_ZoomableImage.qml+21−21
@@ -56,23 +56,23 @@ Rectangle {
56 when: windowShown
56 when: windowShown
57
57
58 function test_loadImage() {
58 function test_loadImage() {
59− var lazyImage = findChild(zoomableImage, "lazyImage");
59+ var imageRenderer = findChild(zoomableImage, "imageRenderer");
60
60
61 zoomableImage.source = widgetData0["source"];
61 zoomableImage.source = widgetData0["source"];
62 zoomableImage.zoomable = widgetData0["zoomable"];
62 zoomableImage.zoomable = widgetData0["zoomable"];
63 waitForRendering(zoomableImage);
63 waitForRendering(zoomableImage);
64− tryCompare(zoomableImage, "imageState", "default");
64+ tryCompare(zoomableImage, "imageStatus", Image.Null);
65
65
66− signalSpy.signalName = "onStateChanged";
66+ signalSpy.signalName = "onStatusChanged";
67− signalSpy.target = lazyImage;
67+ signalSpy.target = imageRenderer;
68 signalSpy.clear();
68 signalSpy.clear();
69
69
70 zoomableImage.source = widgetData1["source"];
70 zoomableImage.source = widgetData1["source"];
71 zoomableImage.zoomable = widgetData1["zoomable"];
71 zoomableImage.zoomable = widgetData1["zoomable"];
72− waitForRendering(lazyImage);
72+ waitForRendering(imageRenderer);
73− tryCompareFunction(function() { return get_filename(lazyImage.source.toString()) === get_filename(widgetData1["source"]); }, true);
73+ tryCompareFunction(function() { return get_filename(imageRenderer.source.toString()) === get_filename(widgetData1["source"]); }, true);
74 waitForRendering(zoomableImage);
74 waitForRendering(zoomableImage);
75− tryCompare(zoomableImage, "imageState", "ready");
75+ tryCompare(zoomableImage, "imageStatus", Image.Ready);
76 compare (signalSpy.count, 1);
76 compare (signalSpy.count, 1);
77 }
77 }
78
78
@@ -84,15 +84,15 @@ Rectangle {
84
84
85 function test_mousewheel() {
85 function test_mousewheel() {
86 var image = findChild(zoomableImage, "image");
86 var image = findChild(zoomableImage, "image");
87− var lazyImage = findChild(zoomableImage, "lazyImage");
87+ var imageRenderer = findChild(zoomableImage, "imageRenderer");
88 var flickable = findChild(zoomableImage, "flickable");
88 var flickable = findChild(zoomableImage, "flickable");
89
89
90 zoomableImage.source = widgetData2["source"];
90 zoomableImage.source = widgetData2["source"];
91 zoomableImage.zoomable = true;
91 zoomableImage.zoomable = true;
92 waitForRendering(zoomableImage);
92 waitForRendering(zoomableImage);
93
93
94− tryCompare(zoomableImage, "imageState", "ready");
94+ tryCompare(zoomableImage, "imageStatus", Image.Ready);
95− tryCompareFunction(function() { return get_filename(lazyImage.source.toString()) === get_filename(widgetData2["source"]); }, true);
95+ tryCompareFunction(function() { return get_filename(imageRenderer.source.toString()) === get_filename(widgetData2["source"]); }, true);
96 waitForRendering(image);
96 waitForRendering(image);
97
97
98 // move mouse to center
98 // move mouse to center
@@ -105,16 +105,16 @@ Rectangle {
105 for (var i=0; i<10; i++) {
105 for (var i=0; i<10; i++) {
106 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, 10);
106 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, 10);
107 tryCompare(image, "scale", oldScale + (i + 1) * 0.1);
107 tryCompare(image, "scale", oldScale + (i + 1) * 0.1);
108− compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
108+ compare(flickable.contentWidth, Math.max(imageRenderer.width * image.scale, flickable.width));
109− compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
109+ compare(flickable.contentHeight, Math.max(imageRenderer.height * image.scale, flickable.height));
110 }
110 }
111
111
112 // zoom out
112 // zoom out
113 for (var i=0; i<10; i++) {
113 for (var i=0; i<10; i++) {
114 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, -10);
114 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, -10);
115 tryCompare(image, "scale", oldScale + 1.0 - (i + 1) * 0.1);
115 tryCompare(image, "scale", oldScale + 1.0 - (i + 1) * 0.1);
116− compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
116+ compare(flickable.contentWidth, Math.max(imageRenderer.width * image.scale, flickable.width));
117− compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
117+ compare(flickable.contentHeight, Math.max(imageRenderer.height * image.scale, flickable.height));
118 }
118 }
119 }
119 }
120 }
120 }
@@ -135,7 +135,7 @@ Rectangle {
135
135
136 function test_pinch(data) {
136 function test_pinch(data) {
137 var image = findChild(zoomableImage, "image");
137 var image = findChild(zoomableImage, "image");
138− var lazyImage = findChild(zoomableImage, "lazyImage");
138+ var imageRenderer = findChild(zoomableImage, "imageRenderer");
139 var flickable = findChild(zoomableImage, "flickable");
139 var flickable = findChild(zoomableImage, "flickable");
140
140
141 signalSpy.signalName = "onScaleChanged";
141 signalSpy.signalName = "onScaleChanged";
@@ -146,8 +146,8 @@ Rectangle {
146 zoomableImage.zoomable = data.zoomable;
146 zoomableImage.zoomable = data.zoomable;
147 waitForRendering(zoomableImage);
147 waitForRendering(zoomableImage);
148
148
149− tryCompare(zoomableImage, "imageState", "ready");
149+ tryCompare(zoomableImage, "imageStatus", Image.Ready);
150− tryCompareFunction(function() { return get_filename(lazyImage.source.toString()) === get_filename(data.source); }, true);
150+ tryCompareFunction(function() { return get_filename(imageRenderer.source.toString()) === get_filename(data.source); }, true);
151 waitForRendering(image);
151 waitForRendering(image);
152
152
153 var x1Start = zoomableImage.width * 2 / 6;
153 var x1Start = zoomableImage.width * 2 / 6;
@@ -179,15 +179,15 @@ Rectangle {
179 compare(newScale > oldScale, data.answer2, "scale factor didn't changed");
179 compare(newScale > oldScale, data.answer2, "scale factor didn't changed");
180 compare(signalSpy.count == 0, data.answer3, "scale signal count error");
180 compare(signalSpy.count == 0, data.answer3, "scale signal count error");
181 compare(image.scale, newScale, "scale factor error");
181 compare(image.scale, newScale, "scale factor error");
182− compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
182+ compare(flickable.contentWidth, Math.max(imageRenderer.width * image.scale, flickable.width));
183− compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
183+ compare(flickable.contentHeight, Math.max(imageRenderer.height * image.scale, flickable.height));
184
184
185 wait(3000); // have to delay between two consequent pinch event.
185 wait(3000); // have to delay between two consequent pinch event.
186 // pinch zoom-out
186 // pinch zoom-out
187 touchPinch(zoomableImage, x1End, y1End, x1Start, y1Start, x2End, y2End, x2Start, y2Start);
187 touchPinch(zoomableImage, x1End, y1End, x1Start, y1Start, x2End, y2End, x2Start, y2Start);
188 tryCompare(image, "scale", oldScale);
188 tryCompare(image, "scale", oldScale);
189− compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
189+ compare(flickable.contentWidth, Math.max(imageRenderer.width * image.scale, flickable.width));
190− compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
190+ compare(flickable.contentHeight, Math.max(imageRenderer.height * image.scale, flickable.height));
191 }
191 }
192 }
192 }
193 }
193 }
tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+25−8
@@ -36,26 +36,43 @@ Rectangle {
36
36
37 PreviewZoomableImage {
37 PreviewZoomableImage {
38 id: zoomableImage
38 id: zoomableImage
39− anchors.fill: parent
39+ anchors.centerIn: parent
40− widgetData: widgetData1
40+ widgetData: widgetData0
41 }
41 }
42
42
43 UT.UnityTestCase {
43 UT.UnityTestCase {
44 name: "PreviewZoomableImageTest"
44 name: "PreviewZoomableImageTest"
45 when: windowShown
45 when: windowShown
46
46
47+ property Item lazyImage: findChild(zoomableImage, "lazyImage");
48+ property Item zoomableImageContainer: findChild(zoomableImage.rootItem, "zoomableImageContainer");
49+
47− function test_loadImage() {
50+ function cleanup() {
51+ zoomableImageContainer.visible = false;
52+ zoomableImage.widgetData = widgetData0;
48− var image = findChild(zoomableImage, "image");
53+ waitForRendering(zoomableImage);
54+ }
49
55
56+ function test_loadImage() {
50 zoomableImage.widgetData = widgetData0;
57 zoomableImage.widgetData = widgetData0;
51 waitForRendering(zoomableImage);
58 waitForRendering(zoomableImage);
52− waitForRendering(image);
59+ waitForRendering(lazyImage);
53− tryCompare(image, "imageState", "ready");
60+ tryCompare(lazyImage, "state", "ready");
54
61
55 zoomableImage.widgetData = widgetData1;
62 zoomableImage.widgetData = widgetData1;
56 waitForRendering(zoomableImage);
63 waitForRendering(zoomableImage);
57− waitForRendering(image);
64+ waitForRendering(lazyImage);
58− tryCompare(image, "imageState", "default");
65+ tryCompare(lazyImage, "state", "default");
66+ }
67+
68+ function test_1zoomableImageOpenClose() {
69+ var zoomableImageCloseButton = findChild(zoomableImageContainer, "zoomableImageCloseButton");
70+ mouseClick(lazyImage, lazyImage.width / 2, lazyImage.height / 2);
71+ tryCompare(zoomableImageContainer, "visible", true);
72+ tryCompare(zoomableImageContainer, "scale", 1.0);
73+ tryCompare(zoomableImageCloseButton, "visible", true);
74+ mouseClick(zoomableImageCloseButton, zoomableImageCloseButton.width / 2, zoomableImageCloseButton.height / 2);
75+ tryCompare(zoomableImageContainer, "visible", false);
59 }
76 }
60 }
77 }
61}
78}