Merge lp:~fboucault/camera-app/desktop_better_responsiveness into lp:camera-app
- desktop_better_responsiveness
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Florian Boucault |
Approved revision: | 591 |
Merged at revision: | 590 |
Proposed branch: | lp:~fboucault/camera-app/desktop_better_responsiveness |
Merge into: | lp:camera-app |
Diff against target: |
368 lines (+160/-38) 8 files modified
PhotogridView.qml (+11/-10) ResponsiveGridView.qml (+108/-0) ViewFinderGeometry.qml (+6/-2) ViewFinderView.qml (+1/-0) camera-app.qml (+26/-8) cameraapplication.cpp (+5/-15) cameraapplication.h (+2/-2) tests/autopilot/camera_app/emulators/main_window.py (+1/-1) |
To merge this branch: | bzr merge lp:~fboucault/camera-app/desktop_better_responsiveness |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Needs Fixing | |
Ubuntu Phablet Team | Pending | ||
Review via email: mp+275721@code.launchpad.net |
Commit message
Desktop convergence fixes:
- Fix viewfinder positioning.
- Resize window to follow viewfinder's aspect ratio.
- Made photo grid responsive.
Description of the change
- 588. By Florian Boucault
-
Added missing properties
- 589. By Florian Boucault
-
Reposition content appropriately when resizing window.
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:589
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 590. By Florian Boucault
-
Fix viewfinder positioning properly.
- 591. By Florian Boucault
-
Fix positionContent
AtRatio in INVERTED_LANDSCAPE
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:591
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'PhotogridView.qml' |
2 | --- PhotogridView.qml 2015-10-22 12:24:01 +0000 |
3 | +++ PhotogridView.qml 2015-10-27 10:36:20 +0000 |
4 | @@ -25,7 +25,6 @@ |
5 | Item { |
6 | id: photogridView |
7 | |
8 | - property int itemsPerRow: 3 |
9 | property var model |
10 | signal photoClicked(int index) |
11 | signal photoPressAndHold(int index) |
12 | @@ -63,7 +62,7 @@ |
13 | function exit() { |
14 | } |
15 | |
16 | - GridView { |
17 | + ResponsiveGridView { |
18 | id: gridView |
19 | anchors.fill: parent |
20 | // FIXME: prevent the header from overlapping the beginning of the grid |
21 | @@ -73,7 +72,7 @@ |
22 | width: gridView.width |
23 | height: headerHeight |
24 | } |
25 | - |
26 | + |
27 | Component.onCompleted: { |
28 | // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition |
29 | // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration |
30 | @@ -82,16 +81,18 @@ |
31 | flickDeceleration = flickDeceleration * scaleFactor; |
32 | } |
33 | |
34 | - cellWidth: width / photogridView.itemsPerRow |
35 | - cellHeight: cellWidth |
36 | + minimumHorizontalSpacing: units.dp(2) |
37 | + maximumNumberOfColumns: 100 |
38 | + delegateWidth: units.gu(13) |
39 | + delegateHeight: units.gu(13) |
40 | |
41 | model: photogridView.model |
42 | delegate: Item { |
43 | id: cellDelegate |
44 | objectName: "mediaItem" + index |
45 | |
46 | - width: GridView.view.cellWidth |
47 | - height: GridView.view.cellHeight |
48 | + width: gridView.cellWidth |
49 | + height: gridView.cellHeight |
50 | |
51 | property bool isVideo: MimeTypeMapper.mimeTypeToContentType(fileType) === ContentType.Videos |
52 | |
53 | @@ -100,13 +101,13 @@ |
54 | property real margin: units.dp(2) |
55 | anchors { |
56 | top: parent.top |
57 | - topMargin: index < photogridView.itemsPerRow ? 0 : margin/2 |
58 | + topMargin: index < photogridView.columns ? 0 : margin/2 |
59 | bottom: parent.bottom |
60 | bottomMargin: margin/2 |
61 | left: parent.left |
62 | - leftMargin: index % photogridView.itemsPerRow == 0 ? 0 : margin/2 |
63 | + leftMargin: index % photogridView.columns == 0 ? 0 : margin/2 |
64 | right: parent.right |
65 | - rightMargin: index % photogridView.itemsPerRow == photogridView.itemsPerRow - 1 ? 0 : margin/2 |
66 | + rightMargin: index % photogridView.columns == photogridView.columns - 1 ? 0 : margin/2 |
67 | } |
68 | |
69 | asynchronous: true |
70 | |
71 | === added file 'ResponsiveGridView.qml' |
72 | --- ResponsiveGridView.qml 1970-01-01 00:00:00 +0000 |
73 | +++ ResponsiveGridView.qml 2015-10-27 10:36:20 +0000 |
74 | @@ -0,0 +1,108 @@ |
75 | +/* |
76 | + * Copyright (C) 2013 Canonical, Ltd. |
77 | + * |
78 | + * This program is free software; you can redistribute it and/or modify |
79 | + * it under the terms of the GNU General Public License as published by |
80 | + * the Free Software Foundation; version 3. |
81 | + * |
82 | + * This program is distributed in the hope that it will be useful, |
83 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
84 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
85 | + * GNU General Public License for more details. |
86 | + * |
87 | + * You should have received a copy of the GNU General Public License |
88 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
89 | + */ |
90 | + |
91 | +import QtQuick 2.3 |
92 | +import Ubuntu.Components 0.1 |
93 | + |
94 | +// FIXME: this is a copy of Unity8's ResponsiveGridView |
95 | + |
96 | +/* |
97 | + Essentially a GridView where you can specify the maximum number of columns it can have. |
98 | + */ |
99 | +Item { |
100 | + property int minimumHorizontalSpacing: units.gu(0.5) |
101 | + // property int minimumNumberOfColumns: 2 // FIXME: not implemented |
102 | + property int maximumNumberOfColumns: 6 |
103 | + readonly property int columns: gridView.columns |
104 | + property alias verticalSpacing: gridView.verticalSpacing |
105 | + readonly property alias margins: gridView.margin |
106 | + property int delegateWidth |
107 | + property int delegateHeight |
108 | + property alias model: gridView.model |
109 | + property alias delegate: gridView.delegate |
110 | + readonly property int cellWidth: gridView.cellWidth |
111 | + readonly property int cellHeight: gridView.cellHeight |
112 | + readonly property int totalContentHeight: { |
113 | + if (gridView.model) { |
114 | + return contentHeightForRows(Math.ceil(gridView.model.count / columns), cellHeight) |
115 | + } else { |
116 | + return 0; |
117 | + } |
118 | + } |
119 | + property alias interactive: gridView.interactive |
120 | + readonly property alias flicking: gridView.flicking |
121 | + readonly property alias moving: gridView.moving |
122 | + readonly property alias pressDelay: gridView.pressDelay |
123 | + readonly property alias originY: gridView.originY |
124 | + property alias displayMarginBeginning: gridView.displayMarginBeginning |
125 | + property alias displayMarginEnd: gridView.displayMarginEnd |
126 | + property alias highlightIndex: gridView.highlightIndex |
127 | + property alias cacheBuffer: gridView.cacheBuffer |
128 | + readonly property alias currentItem: gridView.currentItem |
129 | + property alias header: gridView.header |
130 | + property alias maximumFlickVelocity: gridView.maximumFlickVelocity |
131 | + property alias flickDeceleration: gridView.flickDeceleration |
132 | + |
133 | + function contentHeightForRows(rows, height) { |
134 | + return rows * height |
135 | + } |
136 | + |
137 | + function positionViewAtIndex(index, mode) { |
138 | + gridView.positionViewAtIndex(index, mode); |
139 | + } |
140 | + |
141 | + GridView { |
142 | + id: gridView |
143 | + objectName: "responsiveGridViewGrid" |
144 | + anchors { |
145 | + fill: parent |
146 | + leftMargin: margin/2 |
147 | + rightMargin: margin/2 |
148 | + } |
149 | + clip: parent.height != totalContentHeight |
150 | + |
151 | + function pixelToGU(value) { |
152 | + return Math.floor(value / units.gu(1)); |
153 | + } |
154 | + |
155 | + function spacingForColumns(columns) { |
156 | + // spacing between columns as an integer number of GU, the remainder goes in the margins |
157 | + var spacingGU = pixelToGU(allocatableHorizontalSpace / columns); |
158 | + return units.gu(spacingGU); |
159 | + } |
160 | + |
161 | + function columnsForSpacing(spacing) { |
162 | + // minimum margin is half of the spacing |
163 | + return Math.max(1, Math.floor(parent.width / (delegateWidth + spacing))); |
164 | + } |
165 | + |
166 | + property real allocatableHorizontalSpace: parent.width - columns * delegateWidth |
167 | + property int columns: Math.min(columnsForSpacing(minimumHorizontalSpacing), maximumNumberOfColumns) |
168 | + property real horizontalSpacing: spacingForColumns(columns) |
169 | + property real verticalSpacing: horizontalSpacing |
170 | + property int margin: allocatableHorizontalSpace - columns * horizontalSpacing |
171 | + property int highlightIndex: -1 |
172 | + |
173 | + cellWidth: delegateWidth + horizontalSpacing |
174 | + cellHeight: delegateHeight + verticalSpacing |
175 | + |
176 | + onHighlightIndexChanged: { |
177 | + if (highlightIndex != -1) { |
178 | + currentIndex = highlightIndex |
179 | + } |
180 | + } |
181 | + } |
182 | +} |
183 | |
184 | === modified file 'ViewFinderGeometry.qml' |
185 | --- ViewFinderGeometry.qml 2015-10-22 12:21:14 +0000 |
186 | +++ ViewFinderGeometry.qml 2015-10-27 10:36:20 +0000 |
187 | @@ -18,6 +18,8 @@ |
188 | |
189 | Item { |
190 | property size cameraResolution; |
191 | + property bool cameraResolutionValid: cameraResolution.width != -1 |
192 | + && cameraResolution.height != -1 |
193 | property int viewFinderHeight; |
194 | property int viewFinderWidth; |
195 | property int viewFinderOrientation; |
196 | @@ -30,6 +32,8 @@ |
197 | property real widthScale: viewFinderWidth / __cameraWidth |
198 | property real heightScale: viewFinderHeight / __cameraHeight |
199 | |
200 | - width: (widthScale <= heightScale) ? viewFinderWidth : __cameraWidth * heightScale |
201 | - height: (widthScale <= heightScale) ? __cameraHeight * widthScale : viewFinderHeight |
202 | + width: cameraResolutionValid ? ((widthScale <= heightScale) ? viewFinderWidth : __cameraWidth * heightScale) |
203 | + : viewFinderWidth |
204 | + height: cameraResolutionValid ? ((widthScale <= heightScale) ? __cameraHeight * widthScale : viewFinderHeight) |
205 | + : viewFinderHeight |
206 | } |
207 | |
208 | === modified file 'ViewFinderView.qml' |
209 | --- ViewFinderView.qml 2015-10-22 12:21:14 +0000 |
210 | +++ ViewFinderView.qml 2015-10-27 10:36:20 +0000 |
211 | @@ -29,6 +29,7 @@ |
212 | property bool touchAcquired: viewFinderOverlay.touchAcquired || camera.videoRecorder.recorderState == CameraRecorder.RecordingState |
213 | property bool inView |
214 | property alias captureMode: camera.captureMode |
215 | + property real aspectRatio: viewFinder.sourceRect.width / viewFinder.sourceRect.height |
216 | signal photoTaken(string filePath) |
217 | signal videoShot(string filePath) |
218 | |
219 | |
220 | === modified file 'camera-app.qml' |
221 | --- camera-app.qml 2015-10-26 13:19:14 +0000 |
222 | +++ camera-app.qml 2015-10-27 10:36:20 +0000 |
223 | @@ -23,13 +23,13 @@ |
224 | import Ubuntu.Content 0.1 |
225 | import CameraApp 0.1 |
226 | |
227 | -Item { |
228 | +Window { |
229 | id: main |
230 | objectName: "main" |
231 | - width: units.gu(40) |
232 | - height: units.gu(71) |
233 | -// width: application.desktopMode ? units.gu(120) : (Screen.primaryOrientation === Qt.PortraitOrientation ? units.gu(40) : units.gu(80)) |
234 | -// height: application.desktopMode ? units.gu(60) : (Screen.primaryOrientation === Qt.PortraitOrientation ? units.gu(80) : units.gu(40)) |
235 | + width: height * viewFinderView.aspectRatio |
236 | + height: units.gu(80) |
237 | + color: "black" |
238 | + title: "Camera" |
239 | |
240 | UnityActions.ActionManager { |
241 | actions: [ |
242 | @@ -63,6 +63,11 @@ |
243 | |
244 | Component.onCompleted: { |
245 | i18n.domain = "camera-app"; |
246 | + if (!application.desktopMode) { |
247 | + main.showFullScreen(); |
248 | + } else { |
249 | + main.show(); |
250 | + } |
251 | } |
252 | |
253 | |
254 | @@ -91,7 +96,7 @@ |
255 | galleryView.y = 0; |
256 | viewFinderView.x = 0; |
257 | viewFinderView.y = 0; |
258 | - viewSwitcher.contentX = viewSwitcher.ratio * viewSwitcher.contentWidth; |
259 | + viewSwitcher.positionContentAtRatio(viewSwitcher.ratio) |
260 | viewSwitcher.ratio = Qt.binding(function() { return viewSwitcher.contentX / viewSwitcher.contentWidth }); |
261 | } |
262 | } |
263 | @@ -107,7 +112,7 @@ |
264 | galleryView.y = Qt.binding(function() { return viewFinderView.height + viewSwitcher.panesMargin }); |
265 | viewFinderView.x = 0; |
266 | viewFinderView.y = 0; |
267 | - viewSwitcher.contentY = viewSwitcher.ratio * viewSwitcher.contentHeight; |
268 | + viewSwitcher.positionContentAtRatio(viewSwitcher.ratio) |
269 | viewSwitcher.ratio = Qt.binding(function() { return viewSwitcher.contentY / viewSwitcher.contentHeight }); |
270 | } |
271 | } |
272 | @@ -123,7 +128,7 @@ |
273 | galleryView.y = 0; |
274 | viewFinderView.x = 0; |
275 | viewFinderView.y = Qt.binding(function() { return galleryView.height + viewSwitcher.panesMargin }); |
276 | - viewSwitcher.contentY = (0.5 - viewSwitcher.ratio) * viewSwitcher.contentHeight; |
277 | + viewSwitcher.positionContentAtRatio(viewSwitcher.ratio) |
278 | viewSwitcher.ratio = Qt.binding(function() { return 0.5 - viewSwitcher.contentY / viewSwitcher.contentHeight }); |
279 | } |
280 | } |
281 | @@ -177,6 +182,19 @@ |
282 | } |
283 | } |
284 | |
285 | + function positionContentAtRatio(ratio) { |
286 | + if (state == "PORTRAIT") { |
287 | + viewSwitcher.contentX = ratio * viewSwitcher.contentWidth; |
288 | + } else if (state == "LANDSCAPE") { |
289 | + viewSwitcher.contentY = ratio * viewSwitcher.contentHeight; |
290 | + } else if (state == "INVERTED_LANDSCAPE") { |
291 | + viewSwitcher.contentY = (0.5 - ratio) * viewSwitcher.contentHeight; |
292 | + } |
293 | + } |
294 | + |
295 | + onContentWidthChanged: positionContentAtRatio(viewSwitcher.ratio) |
296 | + onContentHeightChanged: positionContentAtRatio(viewSwitcher.ratio) |
297 | + |
298 | onMovementEnded: { |
299 | // go to a rest position as soon as user stops interacting with the Flickable |
300 | settle(); |
301 | |
302 | === modified file 'cameraapplication.cpp' |
303 | --- cameraapplication.cpp 2015-01-23 19:38:32 +0000 |
304 | +++ cameraapplication.cpp 2015-10-27 10:36:20 +0000 |
305 | @@ -85,23 +85,13 @@ |
306 | Qt::InvertedPortraitOrientation | |
307 | Qt::InvertedLandscapeOrientation); |
308 | |
309 | - m_view.reset(new QQuickView()); |
310 | - m_view->setResizeMode(QQuickView::SizeRootObjectToView); |
311 | - m_view->setTitle("Camera"); |
312 | - m_view->setColor("black"); |
313 | - m_view->rootContext()->setContextProperty("application", this); |
314 | - m_view->engine()->setBaseUrl(QUrl::fromLocalFile(cameraAppDirectory())); |
315 | - m_view->engine()->addImportPath(cameraAppImportDirectory()); |
316 | + m_engine.reset(new QQmlApplicationEngine()); |
317 | + m_engine->rootContext()->setContextProperty("application", this); |
318 | + m_engine->setBaseUrl(QUrl::fromLocalFile(cameraAppDirectory())); |
319 | + m_engine->addImportPath(cameraAppImportDirectory()); |
320 | qDebug() << "Import path added" << cameraAppImportDirectory(); |
321 | qDebug() << "Camera app directory" << cameraAppDirectory(); |
322 | - QObject::connect(m_view->engine(), SIGNAL(quit()), this, SLOT(quit())); |
323 | - m_view->setSource(QUrl::fromLocalFile(sourceQml())); |
324 | - |
325 | - //run fullscreen if specified at command line or not in DESKTOP_MODE (i.e. on a device) |
326 | - if (arguments().contains(QLatin1String("--fullscreen")) || !isDesktopMode()) |
327 | - m_view->showFullScreen(); |
328 | - else |
329 | - m_view->show(); |
330 | + m_engine->load(QUrl::fromLocalFile(sourceQml())); |
331 | |
332 | return true; |
333 | } |
334 | |
335 | === modified file 'cameraapplication.h' |
336 | --- cameraapplication.h 2014-11-13 09:39:37 +0000 |
337 | +++ cameraapplication.h 2015-10-27 10:36:20 +0000 |
338 | @@ -20,7 +20,7 @@ |
339 | #ifndef CAMERAAPPLICATION_H |
340 | #define CAMERAAPPLICATION_H |
341 | |
342 | -#include <QtQuick/QQuickView> |
343 | +#include <QtQml/QQmlApplicationEngine> |
344 | #include <QGuiApplication> |
345 | |
346 | class QDate; |
347 | @@ -54,7 +54,7 @@ |
348 | void removableStoragePresentChanged(); |
349 | |
350 | private: |
351 | - QScopedPointer<QQuickView> m_view; |
352 | + QScopedPointer<QQmlApplicationEngine> m_engine; |
353 | }; |
354 | |
355 | #endif // CAMERAAPPLICATION_H |
356 | |
357 | === modified file 'tests/autopilot/camera_app/emulators/main_window.py' |
358 | --- tests/autopilot/camera_app/emulators/main_window.py 2015-05-15 07:40:37 +0000 |
359 | +++ tests/autopilot/camera_app/emulators/main_window.py 2015-10-27 10:36:20 +0000 |
360 | @@ -18,7 +18,7 @@ |
361 | |
362 | def get_qml_view(self): |
363 | """Get the main QML view""" |
364 | - return self.app.wait_select_single("QQuickView") |
365 | + return self.get_root() |
366 | |
367 | def get_root(self): |
368 | """Returns the root QML Item""" |
FAILED: Continuous integration, rev:587 jenkins. qa.ubuntu. com/job/ camera- app-ci/ 441/ jenkins. qa.ubuntu. com/job/ camera- app-vivid- amd64-ci/ 137/console jenkins. qa.ubuntu. com/job/ camera- app-vivid- armhf-ci/ 137/console jenkins. qa.ubuntu. com/job/ camera- app-vivid- i386-ci/ 137/console jenkins. qa.ubuntu. com/job/ generic- click-autopilot -vivid- touch/282 jenkins. qa.ubuntu. com/job/ generic- click-autopilot -runner- mako/935 jenkins. qa.ubuntu. com/job/ generic- click-builder- vivid-armhf/ 764 s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 24565
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/camera- app-ci/ 441/rebuild
http://