Merge lp:~libqtelegram-team/telegram-app/app-dev-zoom-preview into lp:telegram-app/app-dev

Proposed by Michał Karnicki
Status: Merged
Approved by: Roberto Mier Escandon
Approved revision: 271
Merged at revision: 271
Proposed branch: lp:~libqtelegram-team/telegram-app/app-dev-zoom-preview
Merge into: lp:telegram-app/app-dev
Diff against target: 336 lines (+249/-37)
2 files modified
components/SingleMediaViewer.qml (+219/-0)
ui/PreviewPage.qml (+30/-37)
To merge this branch: bzr merge lp:~libqtelegram-team/telegram-app/app-dev-zoom-preview
Reviewer Review Type Date Requested Status
Roberto Mier Escandon (community) Approve
Review via email: mp+251675@code.launchpad.net

Description of the change

Added preview pinch-to-zoom.

Most of the SingleMediaViewer.qml code was taken from our gallery-app.

For images:
- pinch to zoom
- double tap to zoom

For video:
- playback starts automatically
- touch to pause/resume
- double tap to restart playback from beginning

To post a comment you must log in.
Revision history for this message
Roberto Mier Escandon (rmescandon) wrote :

Absolutely awesome!. Tested Ok

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'components/SingleMediaViewer.qml'
2--- components/SingleMediaViewer.qml 1970-01-01 00:00:00 +0000
3+++ components/SingleMediaViewer.qml 2015-03-04 00:05:38 +0000
4@@ -0,0 +1,219 @@
5+/*
6+ * Copyright 2015 Canonical Ltd.
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+import QtQuick 2.2
22+import QtMultimedia 5.0
23+import Ubuntu.Components 1.0
24+
25+Item {
26+ id: viewer
27+ property bool pinchInProgress: zoomPinchArea.active
28+ property var mediaSource
29+ property real maxDimension
30+ property bool showThumbnail: true
31+
32+ property bool isVideo: videoPreviewSource !== ""
33+ property bool userInteracting: pinchInProgress || flickable.sizeScale != 1.0
34+ property bool fullyZoomed: flickable.sizeScale == zoomPinchArea.maximumZoom
35+ property bool fullyUnzoomed: flickable.sizeScale == zoomPinchArea.minimumZoom
36+
37+ property alias paintedHeight: image.paintedHeight
38+ property alias paintedWidth: image.paintedWidth
39+
40+ signal clicked()
41+
42+ function zoomIn(centerX, centerY, factor) {
43+ flickable.scaleCenterX = centerX / (flickable.sizeScale * flickable.width);
44+ flickable.scaleCenterY = centerY / (flickable.sizeScale * flickable.height);
45+ flickable.sizeScale = factor;
46+ }
47+
48+ function zoomOut() {
49+ if (flickable.sizeScale != 1.0) {
50+ flickable.scaleCenterX = flickable.contentX / flickable.width / (flickable.sizeScale - 1);
51+ flickable.scaleCenterY = flickable.contentY / flickable.height / (flickable.sizeScale - 1);
52+ flickable.sizeScale = 1.0;
53+ }
54+ }
55+
56+ function reset() {
57+ if (viewer.isVideo) {
58+ // Intentionally pausing, not stopping, as resuming is faster.
59+ videoPreview.pause();
60+ } else {
61+ zoomOut();
62+ }
63+ }
64+
65+ ActivityIndicator {
66+ id: activityIndicator
67+ anchors.centerIn: parent
68+ visible: running && !viewer.isVideo
69+ running: image.status != Image.Ready
70+ }
71+
72+ PinchArea {
73+ id: zoomPinchArea
74+ anchors.fill: parent
75+
76+ property real initialZoom
77+ property real maximumScale: 3.0
78+ property real minimumZoom: 1.0
79+ property real maximumZoom: 3.0
80+ property bool active: false
81+ property var center
82+
83+ onPinchStarted: {
84+ if (viewer.isVideo) return;
85+
86+ active = true;
87+ initialZoom = flickable.sizeScale;
88+ center = zoomPinchArea.mapToItem(media, pinch.startCenter.x, pinch.startCenter.y);
89+ zoomIn(center.x, center.y, initialZoom);
90+ }
91+ onPinchUpdated: {
92+ if (viewer.isVideo) return;
93+
94+ var zoomFactor = MathUtils.clamp(initialZoom * pinch.scale, minimumZoom, maximumZoom);
95+ flickable.sizeScale = zoomFactor;
96+ }
97+ onPinchFinished: {
98+ active = false;
99+ }
100+
101+ Flickable {
102+ id: flickable
103+ anchors.fill: parent
104+ contentWidth: media.width
105+ contentHeight: media.height
106+ contentX: (sizeScale - 1) * scaleCenterX * width
107+ contentY: (sizeScale - 1) * scaleCenterY * height
108+ interactive: !viewer.pinchInProgress
109+
110+ property real sizeScale: 1.0
111+ property real scaleCenterX: 0.0
112+ property real scaleCenterY: 0.0
113+ Behavior on sizeScale {
114+ enabled: !viewer.pinchInProgress
115+ UbuntuNumberAnimation {duration: UbuntuAnimation.FastDuration}
116+ }
117+ Behavior on scaleCenterX {
118+ UbuntuNumberAnimation {duration: UbuntuAnimation.FastDuration}
119+ }
120+ Behavior on scaleCenterY {
121+ UbuntuNumberAnimation {duration: UbuntuAnimation.FastDuration}
122+ }
123+
124+ Item {
125+ id: media
126+
127+ width: flickable.width * flickable.sizeScale
128+ height: flickable.height * flickable.sizeScale
129+
130+ Image {
131+ id: image
132+ anchors.fill: parent
133+ asynchronous: true
134+ cache: false
135+ source: photoPreviewSource
136+ sourceSize {
137+ width: viewer.maxDimension
138+ height: viewer.maxDimension
139+ }
140+ fillMode: Image.PreserveAspectFit
141+ visible: viewer.showThumbnail
142+ opacity: status == Image.Ready ? 1.0 : 0.0
143+ Behavior on opacity { UbuntuNumberAnimation {duration: UbuntuAnimation.FastDuration} }
144+
145+ }
146+
147+ Image {
148+ id: highResolutionImage
149+ anchors.fill: parent
150+ asynchronous: true
151+ cache: false
152+ // Load image using the GalleryStandardImageProvider to ensure EXIF orientation
153+ source: flickable.sizeScale > 1.0 ? photoPreviewSource : ""
154+ sourceSize {
155+ width: width
156+ height: height
157+ }
158+ fillMode: Image.PreserveAspectFit
159+ }
160+
161+ MediaPlayer {
162+ id: videoPreview
163+
164+ property bool isPlaying: playbackState === MediaPlayer.PlayingState
165+
166+ autoLoad: true
167+ autoPlay: true
168+ source: videoPreviewSource
169+ }
170+
171+ VideoOutput {
172+ id: videoOutput
173+ anchors.fill: parent
174+ visible: videoPreviewSource !== ""
175+ source: videoPreview
176+ }
177+ }
178+
179+ Icon {
180+ width: units.gu(5)
181+ height: units.gu(5)
182+ anchors.centerIn: parent
183+ name: "media-playback-start"
184+ color: "white"
185+ opacity: 0.8
186+ visible: viewer.isVideo && !videoPreview.isPlaying && !activityIndicator.visible
187+ }
188+
189+ MouseArea {
190+ anchors.fill: parent
191+ onDoubleClicked: {
192+ clickTimer.stop();
193+
194+ if (viewer.isVideo) {
195+ // Rewind.
196+ videoPreview.stop();
197+ videoPreview.play();
198+ return;
199+ }
200+ if (flickable.sizeScale < zoomPinchArea.maximumZoom) {
201+ zoomIn(mouse.x, mouse.y, zoomPinchArea.maximumZoom);
202+ } else {
203+ zoomOut();
204+ }
205+ }
206+ onClicked: {
207+ clickTimer.start();
208+ }
209+
210+ Timer {
211+ id: clickTimer
212+ interval: 150
213+ onTriggered: {
214+ if (viewer.isVideo) {
215+ videoPreview.isPlaying ? videoPreview.pause() : videoPreview.play();
216+ }
217+ viewer.clicked();
218+ }
219+ }
220+ }
221+ }
222+ }
223+}
224
225=== modified file 'ui/PreviewPage.qml'
226--- ui/PreviewPage.qml 2015-02-18 10:14:52 +0000
227+++ ui/PreviewPage.qml 2015-03-04 00:05:38 +0000
228@@ -1,18 +1,32 @@
229+/*
230+ * Copyright 2015 Canonical Ltd.
231+ *
232+ * This program is free software; you can redistribute it and/or modify
233+ * it under the terms of the GNU General Public License as published by
234+ * the Free Software Foundation; version 3.
235+ *
236+ * This program is distributed in the hope that it will be useful,
237+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
238+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
239+ * GNU General Public License for more details.
240+ *
241+ * You should have received a copy of the GNU General Public License
242+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
243+ */
244+
245 import QtQuick 2.0
246-import Ubuntu.Components 0.1
247+import Telegram 0.1
248+import Ubuntu.Components 1.0
249+import Ubuntu.Content 0.1
250+
251 import "../components"
252 import "../components/TelegramColors.js" as TelegramColors
253-import Ubuntu.Content 0.1
254-import QtMultimedia 5.0
255-import Telegram 0.1
256
257 TelegramPage {
258 property string senderName: ""
259 property string photoPreviewSource: ""
260 property string videoPreviewSource: ""
261
262- property bool isPlaying: videoPreview.playbackState === MediaPlayer.PlayingState
263-
264 signal bigPhotoUpdated(var id, string bigPhotoUrl);
265
266 id: previewPage
267@@ -40,6 +54,8 @@
268 }
269
270 function save() {
271+ singleMediaViewer.reset();
272+
273 if (photoPreviewSource !== "") {
274 pageStack.push(picker, {
275 "url": photoPreviewSource,
276@@ -56,9 +72,11 @@
277 }
278
279 function share() {
280+ singleMediaViewer.reset();
281+
282 if (photoPreviewSource !== "") {
283 pageStack.push(picker, {
284- "url": photoPreview.source,
285+ "url": photoPreviewSource,
286 "handler": ContentHandler.Share,
287 "contentType": ContentType.Pictures
288 });
289@@ -82,34 +100,10 @@
290 color: TelegramColors.page_background
291 }
292
293- Image {
294- id: photoPreview
295- anchors.fill: parent
296- sourceSize.width: width
297- sourceSize.height: height
298- fillMode: Image.PreserveAspectFit
299-
300- visible: photoPreviewSource !== ""
301- source: photoPreviewSource
302- }
303-
304- MediaPlayer {
305- id: videoPreview
306- autoLoad: true
307- autoPlay: true
308- source: videoPreviewSource !== "" ? videoPreviewSource : ""
309- }
310-
311- VideoOutput {
312- id: videoOutput
313- anchors.fill: parent
314- visible: videoPreviewSource !== ""
315- source: videoPreview
316- }
317-
318- MouseArea {
319- anchors.fill: videoOutput
320- onClicked: isPlaying ? videoPreview.pause() : videoPreview.play()
321+ SingleMediaViewer {
322+ id: singleMediaViewer
323+ anchors.fill: parent
324+ mediaSource: photoPreviewSource
325 }
326
327 /*
328@@ -136,7 +130,6 @@
329 }
330
331 onBigPhotoUpdated: {
332- photoPreview.source = bigPhotoUrl;
333- photoPreview.visible = true;
334+ singleMediaViewer.mediaSource = bigPhotoUrl;
335 }
336 }

Subscribers

People subscribed via source and target branches

to all changes: