Merge lp:~fboucault/camera-app/custom_rotation into lp:camera-app

Proposed by Florian Boucault
Status: Superseded
Proposed branch: lp:~fboucault/camera-app/custom_rotation
Merge into: lp:camera-app
Diff against target: 997 lines (+468/-358)
9 files modified
CircleButton.qml (+3/-2)
OptionButton.qml (+1/-0)
OptionsOverlay.qml (+1/-1)
PhotoRollHint.qml (+30/-28)
Snapshot.qml (+20/-5)
ViewFinderOverlay.qml (+285/-278)
ViewFinderView.qml (+1/-1)
camera-app.desktop.in.in (+1/-0)
camera-app.qml (+126/-43)
To merge this branch: bzr merge lp:~fboucault/camera-app/custom_rotation
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Ubuntu Phablet Team Pending
Review via email: mp+263285@code.launchpad.net

This proposal has been superseded by a proposal from 2015-07-03.

Commit message

Set desktop file flags informing Unity that the camera app is doing its own content rotation when the device rotates.
Manually rotate/position depending on device orientation:
- photo roll
- bottom edge indicators and content
- first photo roll hint
- snapshot animation
- countdown timer

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
568. By Florian Boucault

Manually rotate/position depending on device orientation:
- photo roll
- bottom edge indicators and content
- first photo roll hint
- snapshot animation

569. By Florian Boucault

Tagged bug reports

570. By Florian Boucault

Manually rotate countdown timer depending on device orientation.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
571. By Florian Boucault

Merged lp:~canonical-platform-qa/camera-app/fix1444170-flake8

572. By Florian Boucault

More robust camera_app.tests.test_flash.TestCameraFlash.test_flash_hdr_mutually_exclusive

573. By Florian Boucault

More appropriate bottom edge input area.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CircleButton.qml'
2--- CircleButton.qml 2014-12-05 18:57:32 +0000
3+++ CircleButton.qml 2015-07-03 11:04:00 +0000
4@@ -25,6 +25,7 @@
5 property url iconSource
6 property bool on: true
7 property string label: ""
8+ property bool automaticOrientation: true
9
10 width: units.gu(5)
11 height: width
12@@ -49,7 +50,7 @@
13 color: "white"
14 opacity: button.on ? (button.enabled ? 1.0 : 0.3): 0.5
15 visible: label === ""
16- rotation: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
17+ rotation: automaticOrientation ? Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) : 0
18 Behavior on rotation {
19 RotationAnimator {
20 duration: UbuntuAnimation.BriskDuration
21@@ -69,7 +70,7 @@
22 text: label
23 opacity: button.on ? (button.enabled ? 1.0 : 0.3): 0.5
24 visible: label !== ""
25- rotation: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
26+ rotation: automaticOrientation ? Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) : 0
27 Behavior on rotation {
28 RotationAnimator {
29 duration: UbuntuAnimation.BriskDuration
30
31=== modified file 'OptionButton.qml'
32--- OptionButton.qml 2015-01-27 11:32:45 +0000
33+++ OptionButton.qml 2015-07-03 11:04:00 +0000
34@@ -29,4 +29,5 @@
35 enabled: model.available
36 label: model.label
37 visible: model.visible
38+ automaticOrientation: false
39 }
40
41=== modified file 'OptionsOverlay.qml'
42--- OptionsOverlay.qml 2015-01-27 15:22:59 +0000
43+++ OptionsOverlay.qml 2015-07-03 11:04:00 +0000
44@@ -37,7 +37,7 @@
45
46 columns: 3
47 columnSpacing: units.gu(9.5)
48- rowSpacing: units.gu(4)
49+ rowSpacing: units.gu(3)
50
51 Repeater {
52 model: optionsOverlay.options
53
54=== modified file 'PhotoRollHint.qml'
55--- PhotoRollHint.qml 2014-11-05 10:17:23 +0000
56+++ PhotoRollHint.qml 2015-07-03 11:04:00 +0000
57@@ -40,33 +40,35 @@
58 property alias photoRollHintNecessary: photoRollHint.necessary
59 }
60
61- Image {
62- id: hintPictogram
63- anchors {
64- horizontalCenter: parent.horizontalCenter
65- horizontalCenterOffset: units.gu(4)
66- verticalCenter: parent.verticalCenter
67- verticalCenterOffset: -units.gu(1)
68- }
69-
70- asynchronous: true
71- cache: false
72- source: photoRollHint.enabled ? "assets/camera_swipe.png" : ""
73- }
74-
75- Label {
76- id: hintLabel
77-
78- anchors {
79- top: hintPictogram.bottom
80- topMargin: units.gu(5)
81- horizontalCenter: parent.horizontalCenter
82- }
83- width: parent.width - 2 * units.gu(2)
84- horizontalAlignment: Text.AlignHCenter
85- wrapMode: Text.Wrap
86- text: i18n.tr("Swipe left for photo roll")
87- fontSize: "x-large"
88- color: "#ebebeb"
89+ OrientationHelper {
90+ Image {
91+ id: hintPictogram
92+ anchors {
93+ horizontalCenter: parent.horizontalCenter
94+ horizontalCenterOffset: units.gu(4)
95+ verticalCenter: parent.verticalCenter
96+ verticalCenterOffset: -units.gu(1)
97+ }
98+
99+ asynchronous: true
100+ cache: false
101+ source: photoRollHint.enabled ? "assets/camera_swipe.png" : ""
102+ }
103+
104+ Label {
105+ id: hintLabel
106+
107+ anchors {
108+ top: hintPictogram.bottom
109+ topMargin: units.gu(5)
110+ horizontalCenter: parent.horizontalCenter
111+ }
112+ width: parent.width - 2 * units.gu(2)
113+ horizontalAlignment: Text.AlignHCenter
114+ wrapMode: Text.Wrap
115+ text: i18n.tr("Swipe left for photo roll")
116+ fontSize: "x-large"
117+ color: "#ebebeb"
118+ }
119 }
120 }
121
122=== modified file 'Snapshot.qml'
123--- Snapshot.qml 2014-10-09 19:07:35 +0000
124+++ Snapshot.qml 2015-07-03 11:04:00 +0000
125@@ -15,6 +15,7 @@
126 */
127
128 import QtQuick 2.2
129+import QtQuick.Window 2.0
130 import Ubuntu.Components 1.0
131
132 Item {
133@@ -34,11 +35,8 @@
134
135 Item {
136 id: container
137- anchors {
138- top: parent.top
139- bottom: parent.bottom
140- }
141 width: parent.width
142+ height: parent.height
143
144 Image {
145 id: snapshot
146@@ -69,15 +67,32 @@
147 cache: false
148 }
149 }
150+ property int orientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
151+ property var angleToOrientation: {0: "PORTRAIT",
152+ 90: "LANDSCAPE",
153+ 270: "INVERTED_LANDSCAPE"}
154
155 SequentialAnimation {
156 id: shoot
157
158 PropertyAction { target: snapshotRoot; property: "visible"; value: true }
159 PauseAnimation { duration: 150 }
160- XAnimator { target: container; to: container.width + shadow.width; duration: UbuntuAnimation.BriskDuration; easing: UbuntuAnimation.StandardEasing}
161+ XAnimator {
162+ target: container
163+ to: angleToOrientation[orientationAngle] == "PORTRAIT" ? container.width + shadow.width : 0
164+ duration: UbuntuAnimation.BriskDuration
165+ easing: UbuntuAnimation.StandardEasing
166+ }
167+ YAnimator {
168+ target: container
169+ to: angleToOrientation[orientationAngle] == "LANDSCAPE" ? container.height + shadow.width :
170+ angleToOrientation[orientationAngle] == "INVERTED_LANDSCAPE" ? -(container.height + shadow.width) : 0
171+ duration: UbuntuAnimation.BriskDuration
172+ easing: UbuntuAnimation.StandardEasing
173+ }
174 PropertyAction { target: snapshot; property: "source"; value: ""}
175 PropertyAction { target: snapshotRoot; property: "visible"; value: false }
176 PropertyAction { target: container; property: "x"; value: 0 }
177+ PropertyAction { target: container; property: "y"; value: 0 }
178 }
179 }
180
181=== modified file 'ViewFinderOverlay.qml'
182--- ViewFinderOverlay.qml 2015-03-13 00:41:54 +0000
183+++ ViewFinderOverlay.qml 2015-07-03 11:04:00 +0000
184@@ -159,269 +159,289 @@
185 onClicked: optionsOverlayClose()
186 }
187
188- Panel {
189- id: bottomEdge
190- anchors {
191- right: parent.right
192- left: parent.left
193- bottom: parent.bottom
194- }
195- height: optionsOverlayLoader.height
196- onOpenedChanged: optionsOverlayLoader.item.closeValueSelector()
197- enabled: camera.videoRecorder.recorderState == CameraRecorder.StoppedState
198- opacity: enabled ? 1.0 : 0.3
199-
200- Item {
201- /* Use the 'trigger' feature of Panel so that tapping on the Panel
202- has the same effect as tapping outside of it (bottomEdgeClose) */
203- id: clickReceiver
204- anchors.fill: parent
205- function trigger() {
206- optionsOverlayClose();
207- }
208- }
209-
210- property real progress: (bottomEdge.height - bottomEdge.position) / bottomEdge.height
211- property list<ListModel> options: [
212- ListModel {
213- id: gpsOptionsModel
214-
215- property string settingsProperty: "gpsEnabled"
216- property string icon: "location"
217- property string label: ""
218- property bool isToggle: true
219- property int selectedIndex: bottomEdge.indexForValue(gpsOptionsModel, settings.gpsEnabled)
220- property bool available: true
221- property bool visible: true
222- property bool showInIndicators: true
223-
224- ListElement {
225- icon: ""
226- label: QT_TR_NOOP("On")
227- value: true
228- }
229- ListElement {
230- icon: ""
231- label: QT_TR_NOOP("Off")
232- value: false
233- }
234- },
235- ListModel {
236- id: flashOptionsModel
237-
238- property string settingsProperty: "flashMode"
239- property string icon: ""
240- property string label: ""
241- property bool isToggle: false
242- property int selectedIndex: bottomEdge.indexForValue(flashOptionsModel, settings.flashMode)
243- property bool available: camera.advanced.hasFlash
244- property bool visible: camera.captureMode == Camera.CaptureStillImage
245- property bool showInIndicators: true
246-
247- ListElement {
248- icon: "flash-on"
249- label: QT_TR_NOOP("On")
250- value: Camera.FlashOn
251- }
252- ListElement {
253- icon: "flash-auto"
254- label: QT_TR_NOOP("Auto")
255- value: Camera.FlashAuto
256- }
257- ListElement {
258- icon: "flash-off"
259- label: QT_TR_NOOP("Off")
260- value: Camera.FlashOff
261- }
262- },
263- ListModel {
264- id: videoFlashOptionsModel
265-
266- property string settingsProperty: "videoFlashMode"
267- property string icon: ""
268- property string label: ""
269- property bool isToggle: false
270- property int selectedIndex: bottomEdge.indexForValue(videoFlashOptionsModel, settings.videoFlashMode)
271- property bool available: camera.advanced.hasFlash
272- property bool visible: camera.captureMode == Camera.CaptureVideo
273- property bool showInIndicators: true
274-
275- ListElement {
276- icon: "torch-on"
277- label: QT_TR_NOOP("On")
278- value: Camera.FlashVideoLight
279- }
280- ListElement {
281- icon: "torch-off"
282- label: QT_TR_NOOP("Off")
283- value: Camera.FlashOff
284- }
285- },
286- ListModel {
287- id: hdrOptionsModel
288-
289- property string settingsProperty: "hdrEnabled"
290- property string icon: ""
291- property string label: i18n.tr("HDR")
292- property bool isToggle: true
293- property int selectedIndex: bottomEdge.indexForValue(hdrOptionsModel, settings.hdrEnabled)
294- property bool available: camera.advanced.hasHdr
295- property bool visible: true
296- property bool showInIndicators: true
297-
298- ListElement {
299- icon: ""
300- label: QT_TR_NOOP("On")
301- value: true
302- }
303- ListElement {
304- icon: ""
305- label: QT_TR_NOOP("Off")
306- value: false
307- }
308- },
309- ListModel {
310- id: selfTimerOptionsModel
311-
312- property string settingsProperty: "selfTimerDelay"
313- property string icon: ""
314- property string iconSource: "assets/self_timer.svg"
315- property string label: ""
316- property bool isToggle: true
317- property int selectedIndex: bottomEdge.indexForValue(selfTimerOptionsModel, settings.selfTimerDelay)
318- property bool available: true
319- property bool visible: true
320- property bool showInIndicators: true
321-
322- ListElement {
323- icon: ""
324- label: QT_TR_NOOP("Off")
325- value: 0
326- }
327- ListElement {
328- icon: ""
329- label: QT_TR_NOOP("5 seconds")
330- value: 5
331- }
332- ListElement {
333- icon: ""
334- label: QT_TR_NOOP("15 seconds")
335- value: 15
336- }
337- },
338- ListModel {
339- id: encodingQualityOptionsModel
340-
341- property string settingsProperty: "encodingQuality"
342- property string icon: "stock_image"
343- property string label: ""
344- property bool isToggle: false
345- property int selectedIndex: bottomEdge.indexForValue(encodingQualityOptionsModel, settings.encodingQuality)
346- property bool available: true
347- property bool visible: camera.captureMode == Camera.CaptureStillImage
348- property bool showInIndicators: false
349-
350- ListElement {
351- label: QT_TR_NOOP("Fine Quality")
352- value: 4 // QMultimedia.VeryHighQuality
353- }
354- ListElement {
355- label: QT_TR_NOOP("Normal Quality")
356- value: 2 // QMultimedia.NormalQuality
357- }
358- ListElement {
359- label: QT_TR_NOOP("Basic Quality")
360- value: 1 // QMultimedia.LowQuality
361- }
362- },
363- ListModel {
364- id: gridOptionsModel
365-
366- property string settingsProperty: "gridEnabled"
367- property string icon: ""
368- property string iconSource: "assets/grid_lines.svg"
369- property string label: ""
370- property bool isToggle: true
371- property int selectedIndex: bottomEdge.indexForValue(gridOptionsModel, settings.gridEnabled)
372- property bool available: true
373- property bool visible: true
374-
375- ListElement {
376- icon: ""
377- label: QT_TR_NOOP("On")
378- value: true
379- }
380- ListElement {
381- icon: ""
382- label: QT_TR_NOOP("Off")
383- value: false
384- }
385- },
386- ListModel {
387- id: removableStorageOptionsModel
388-
389- property string settingsProperty: "preferRemovableStorage"
390- property string icon: ""
391- property string label: i18n.tr("SD")
392- property bool isToggle: true
393- property int selectedIndex: bottomEdge.indexForValue(removableStorageOptionsModel, settings.preferRemovableStorage)
394- property bool available: application.removableStoragePresent
395- property bool visible: available
396-
397- ListElement {
398- icon: ""
399- label: QT_TR_NOOP("Save to SD Card")
400- value: true
401- }
402- ListElement {
403- icon: ""
404- label: QT_TR_NOOP("Save internally")
405- value: false
406- }
407- },
408- ListModel {
409- id: videoResolutionOptionsModel
410-
411- property string settingsProperty: "videoResolution"
412- property string icon: ""
413- property string label: "HD"
414- property bool isToggle: false
415- property int selectedIndex: bottomEdge.indexForValue(videoResolutionOptionsModel, settings.videoResolution)
416- property bool available: true
417- property bool visible: camera.captureMode == Camera.CaptureVideo
418- property bool showInIndicators: false
419- }
420- ]
421-
422- /* FIXME: application.removableStoragePresent is not updated dynamically.
423- Workaround that by reading it when the bottom edge is opened/closed.
424- */
425- Connections {
426- target: bottomEdge
427- onOpenedChanged: removableStorageOptionsModel.available = application.removableStoragePresent
428- }
429-
430- function indexForValue(model, value) {
431- var i;
432- var element;
433- for (i=0; i<model.count; i++) {
434- element = model.get(i);
435- if (element.value === value) {
436- return i;
437- }
438- }
439-
440- return -1;
441- }
442-
443- BottomEdgeIndicators {
444- options: bottomEdge.options
445+ OrientationHelper {
446+ id: bottomEdgeOrientation
447+ transitionEnabled: bottomEdge.opened
448+
449+ Panel {
450+ id: bottomEdge
451 anchors {
452- horizontalCenter: parent.horizontalCenter
453- bottom: parent.top
454- }
455- opacity: bottomEdge.pressed || bottomEdge.opened ? 0.0 : 1.0
456- Behavior on opacity { UbuntuNumberAnimation {} }
457+ right: parent.right
458+ left: parent.left
459+ bottom: parent.bottom
460+ }
461+ height: optionsOverlayLoader.height
462+ onOpenedChanged: optionsOverlayLoader.item.closeValueSelector()
463+ enabled: camera.videoRecorder.recorderState == CameraRecorder.StoppedState
464+ opacity: enabled ? 1.0 : 0.3
465+
466+ Item {
467+ /* Use the 'trigger' feature of Panel so that tapping on the Panel
468+ has the same effect as tapping outside of it (bottomEdgeClose) */
469+ id: clickReceiver
470+ anchors.fill: parent
471+ function trigger() {
472+ optionsOverlayClose();
473+ }
474+ }
475+
476+ property real progress: (bottomEdge.height - bottomEdge.position) / bottomEdge.height
477+ property list<ListModel> options: [
478+ ListModel {
479+ id: gpsOptionsModel
480+
481+ property string settingsProperty: "gpsEnabled"
482+ property string icon: "location"
483+ property string label: ""
484+ property bool isToggle: true
485+ property int selectedIndex: bottomEdge.indexForValue(gpsOptionsModel, settings.gpsEnabled)
486+ property bool available: true
487+ property bool visible: true
488+ property bool showInIndicators: true
489+
490+ ListElement {
491+ icon: ""
492+ label: QT_TR_NOOP("On")
493+ value: true
494+ }
495+ ListElement {
496+ icon: ""
497+ label: QT_TR_NOOP("Off")
498+ value: false
499+ }
500+ },
501+ ListModel {
502+ id: flashOptionsModel
503+
504+ property string settingsProperty: "flashMode"
505+ property string icon: ""
506+ property string label: ""
507+ property bool isToggle: false
508+ property int selectedIndex: bottomEdge.indexForValue(flashOptionsModel, settings.flashMode)
509+ property bool available: camera.advanced.hasFlash
510+ property bool visible: camera.captureMode == Camera.CaptureStillImage
511+ property bool showInIndicators: true
512+
513+ ListElement {
514+ icon: "flash-on"
515+ label: QT_TR_NOOP("On")
516+ value: Camera.FlashOn
517+ }
518+ ListElement {
519+ icon: "flash-auto"
520+ label: QT_TR_NOOP("Auto")
521+ value: Camera.FlashAuto
522+ }
523+ ListElement {
524+ icon: "flash-off"
525+ label: QT_TR_NOOP("Off")
526+ value: Camera.FlashOff
527+ }
528+ },
529+ ListModel {
530+ id: videoFlashOptionsModel
531+
532+ property string settingsProperty: "videoFlashMode"
533+ property string icon: ""
534+ property string label: ""
535+ property bool isToggle: false
536+ property int selectedIndex: bottomEdge.indexForValue(videoFlashOptionsModel, settings.videoFlashMode)
537+ property bool available: camera.advanced.hasFlash
538+ property bool visible: camera.captureMode == Camera.CaptureVideo
539+ property bool showInIndicators: true
540+
541+ ListElement {
542+ icon: "torch-on"
543+ label: QT_TR_NOOP("On")
544+ value: Camera.FlashVideoLight
545+ }
546+ ListElement {
547+ icon: "torch-off"
548+ label: QT_TR_NOOP("Off")
549+ value: Camera.FlashOff
550+ }
551+ },
552+ ListModel {
553+ id: hdrOptionsModel
554+
555+ property string settingsProperty: "hdrEnabled"
556+ property string icon: ""
557+ property string label: i18n.tr("HDR")
558+ property bool isToggle: true
559+ property int selectedIndex: bottomEdge.indexForValue(hdrOptionsModel, settings.hdrEnabled)
560+ property bool available: camera.advanced.hasHdr
561+ property bool visible: true
562+ property bool showInIndicators: true
563+
564+ ListElement {
565+ icon: ""
566+ label: QT_TR_NOOP("On")
567+ value: true
568+ }
569+ ListElement {
570+ icon: ""
571+ label: QT_TR_NOOP("Off")
572+ value: false
573+ }
574+ },
575+ ListModel {
576+ id: selfTimerOptionsModel
577+
578+ property string settingsProperty: "selfTimerDelay"
579+ property string icon: ""
580+ property string iconSource: "assets/self_timer.svg"
581+ property string label: ""
582+ property bool isToggle: true
583+ property int selectedIndex: bottomEdge.indexForValue(selfTimerOptionsModel, settings.selfTimerDelay)
584+ property bool available: true
585+ property bool visible: true
586+ property bool showInIndicators: true
587+
588+ ListElement {
589+ icon: ""
590+ label: QT_TR_NOOP("Off")
591+ value: 0
592+ }
593+ ListElement {
594+ icon: ""
595+ label: QT_TR_NOOP("5 seconds")
596+ value: 5
597+ }
598+ ListElement {
599+ icon: ""
600+ label: QT_TR_NOOP("15 seconds")
601+ value: 15
602+ }
603+ },
604+ ListModel {
605+ id: encodingQualityOptionsModel
606+
607+ property string settingsProperty: "encodingQuality"
608+ property string icon: "stock_image"
609+ property string label: ""
610+ property bool isToggle: false
611+ property int selectedIndex: bottomEdge.indexForValue(encodingQualityOptionsModel, settings.encodingQuality)
612+ property bool available: true
613+ property bool visible: camera.captureMode == Camera.CaptureStillImage
614+ property bool showInIndicators: false
615+
616+ ListElement {
617+ label: QT_TR_NOOP("Fine Quality")
618+ value: 4 // QMultimedia.VeryHighQuality
619+ }
620+ ListElement {
621+ label: QT_TR_NOOP("Normal Quality")
622+ value: 2 // QMultimedia.NormalQuality
623+ }
624+ ListElement {
625+ label: QT_TR_NOOP("Basic Quality")
626+ value: 1 // QMultimedia.LowQuality
627+ }
628+ },
629+ ListModel {
630+ id: gridOptionsModel
631+
632+ property string settingsProperty: "gridEnabled"
633+ property string icon: ""
634+ property string iconSource: "assets/grid_lines.svg"
635+ property string label: ""
636+ property bool isToggle: true
637+ property int selectedIndex: bottomEdge.indexForValue(gridOptionsModel, settings.gridEnabled)
638+ property bool available: true
639+ property bool visible: true
640+
641+ ListElement {
642+ icon: ""
643+ label: QT_TR_NOOP("On")
644+ value: true
645+ }
646+ ListElement {
647+ icon: ""
648+ label: QT_TR_NOOP("Off")
649+ value: false
650+ }
651+ },
652+ ListModel {
653+ id: removableStorageOptionsModel
654+
655+ property string settingsProperty: "preferRemovableStorage"
656+ property string icon: ""
657+ property string label: i18n.tr("SD")
658+ property bool isToggle: true
659+ property int selectedIndex: bottomEdge.indexForValue(removableStorageOptionsModel, settings.preferRemovableStorage)
660+ property bool available: application.removableStoragePresent
661+ property bool visible: available
662+
663+ ListElement {
664+ icon: ""
665+ label: QT_TR_NOOP("Save to SD Card")
666+ value: true
667+ }
668+ ListElement {
669+ icon: ""
670+ label: QT_TR_NOOP("Save internally")
671+ value: false
672+ }
673+ },
674+ ListModel {
675+ id: videoResolutionOptionsModel
676+
677+ property string settingsProperty: "videoResolution"
678+ property string icon: ""
679+ property string label: "HD"
680+ property bool isToggle: false
681+ property int selectedIndex: bottomEdge.indexForValue(videoResolutionOptionsModel, settings.videoResolution)
682+ property bool available: true
683+ property bool visible: camera.captureMode == Camera.CaptureVideo
684+ property bool showInIndicators: false
685+ }
686+ ]
687+
688+ /* FIXME: application.removableStoragePresent is not updated dynamically.
689+ Workaround that by reading it when the bottom edge is opened/closed.
690+ */
691+ Connections {
692+ target: bottomEdge
693+ onOpenedChanged: removableStorageOptionsModel.available = application.removableStoragePresent
694+ }
695+
696+ function indexForValue(model, value) {
697+ var i;
698+ var element;
699+ for (i=0; i<model.count; i++) {
700+ element = model.get(i);
701+ if (element.value === value) {
702+ return i;
703+ }
704+ }
705+
706+ return -1;
707+ }
708+
709+ BottomEdgeIndicators {
710+ options: bottomEdge.options
711+ anchors {
712+ horizontalCenter: parent.horizontalCenter
713+ bottom: parent.top
714+ }
715+ opacity: bottomEdge.pressed || bottomEdge.opened ? 0.0 : 1.0
716+ Behavior on opacity { UbuntuNumberAnimation {} }
717+ }
718+
719+ Loader {
720+ id: optionsOverlayLoader
721+ anchors {
722+ left: parent.left
723+ right: parent.right
724+ top: parent.top
725+ }
726+ asynchronous: true
727+ sourceComponent: Component {
728+ OptionsOverlay {
729+ options: bottomEdge.options
730+ }
731+ }
732+ }
733 }
734 }
735
736@@ -438,7 +458,7 @@
737 right: parent.right
738 }
739 height: parent.height
740- y: bottomEdge.position - bottomEdge.height
741+ y: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) == 0 ? bottomEdge.position - bottomEdge.height : 0
742 opacity: 1 - bottomEdge.progress
743 visible: opacity != 0.0
744 enabled: visible
745@@ -658,7 +678,9 @@
746 bottom: shootButton.top
747 bottomMargin: units.gu(1)
748 left: parent.left
749+ leftMargin: units.gu(1)
750 right: parent.right
751+ rightMargin: units.gu(1)
752 }
753
754 property real initialZoom
755@@ -777,19 +799,4 @@
756 }
757 }
758 }
759-
760- Loader {
761- id: optionsOverlayLoader
762- anchors {
763- left: parent.left
764- right: parent.right
765- top: controls.bottom
766- }
767- asynchronous: true
768- sourceComponent: Component {
769- OptionsOverlay {
770- options: bottomEdge.options
771- }
772- }
773- }
774 }
775
776=== modified file 'ViewFinderView.qml'
777--- ViewFinderView.qml 2015-01-24 12:52:12 +0000
778+++ ViewFinderView.qml 2015-07-03 11:04:00 +0000
779@@ -296,7 +296,7 @@
780 onInViewChanged: if (!viewFinderView.inView) viewFinderOverlay.controls.cancelTimedShoot()
781 }
782
783- Item {
784+ OrientationHelper {
785 id: timedShootFeedback
786 anchors.fill: parent
787
788
789=== modified file 'camera-app.desktop.in.in'
790--- camera-app.desktop.in.in 2014-09-03 18:52:03 +0000
791+++ camera-app.desktop.in.in 2015-07-03 11:04:00 +0000
792@@ -10,3 +10,4 @@
793 X-Ubuntu-Touch=true
794 X-Ubuntu-Single-Instance=true
795 X-Ubuntu-Default-Department-ID=accessories
796+X-Ubuntu-Rotates-Window-Contents=true
797
798=== modified file 'camera-app.qml'
799--- camera-app.qml 2015-04-24 12:12:23 +0000
800+++ camera-app.qml 2015-07-03 11:04:00 +0000
801@@ -69,10 +69,66 @@
802 Flickable {
803 id: viewSwitcher
804 anchors.fill: parent
805- flickableDirection: Flickable.HorizontalFlick
806+ flickableDirection: state == "PORTRAIT" ? Flickable.HorizontalFlick : Flickable.VerticalFlick
807 boundsBehavior: Flickable.StopAtBounds
808- contentWidth: contentItem.childrenRect.width
809- contentHeight: contentItem.childrenRect.height
810+
811+ property real panesMargin: units.gu(1)
812+ property real ratio
813+ property int orientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
814+ property var angleToOrientation: {0: "PORTRAIT",
815+ 90: "LANDSCAPE",
816+ 270: "INVERTED_LANDSCAPE"}
817+ state: angleToOrientation[orientationAngle]
818+ states: [
819+ State {
820+ name: "PORTRAIT"
821+ StateChangeScript {
822+ script: {
823+ viewSwitcher.ratio = viewSwitcher.ratio;
824+ viewSwitcher.contentWidth = Qt.binding(function() { return viewSwitcher.width * 2 + viewSwitcher.panesMargin });
825+ viewSwitcher.contentHeight = Qt.binding(function() { return viewSwitcher.height });
826+ galleryView.x = Qt.binding(function() { return viewFinderView.width + viewSwitcher.panesMargin });
827+ galleryView.y = 0;
828+ viewFinderView.x = 0;
829+ viewFinderView.y = 0;
830+ viewSwitcher.contentX = viewSwitcher.ratio * viewSwitcher.contentWidth;
831+ viewSwitcher.ratio = Qt.binding(function() { return viewSwitcher.contentX / viewSwitcher.contentWidth });
832+ }
833+ }
834+ },
835+ State {
836+ name: "LANDSCAPE"
837+ StateChangeScript {
838+ script: {
839+ viewSwitcher.ratio = viewSwitcher.ratio;
840+ viewSwitcher.contentWidth = Qt.binding(function() { return viewSwitcher.width });
841+ viewSwitcher.contentHeight = Qt.binding(function() { return viewSwitcher.height * 2 + viewSwitcher.panesMargin });
842+ galleryView.x = 0;
843+ galleryView.y = Qt.binding(function() { return viewFinderView.height + viewSwitcher.panesMargin });
844+ viewFinderView.x = 0;
845+ viewFinderView.y = 0;
846+ viewSwitcher.contentY = viewSwitcher.ratio * viewSwitcher.contentHeight;
847+ viewSwitcher.ratio = Qt.binding(function() { return viewSwitcher.contentY / viewSwitcher.contentHeight });
848+ }
849+ }
850+ },
851+ State {
852+ name: "INVERTED_LANDSCAPE"
853+ StateChangeScript {
854+ script: {
855+ viewSwitcher.ratio = viewSwitcher.ratio;
856+ viewSwitcher.contentWidth = Qt.binding(function() { return viewSwitcher.width });
857+ viewSwitcher.contentHeight = Qt.binding(function() { return viewSwitcher.height * 2 + viewSwitcher.panesMargin });
858+ galleryView.x = 0;
859+ galleryView.y = 0;
860+ viewFinderView.x = 0;
861+ viewFinderView.y = Qt.binding(function() { return galleryView.height + viewSwitcher.panesMargin });
862+ viewSwitcher.contentY = (0.5 - viewSwitcher.ratio) * viewSwitcher.contentHeight;
863+ viewSwitcher.ratio = Qt.binding(function() { return 0.5 - viewSwitcher.contentY / viewSwitcher.contentHeight });
864+ }
865+ }
866+ }
867+ ]
868 interactive: !viewFinderView.touchAcquired && !galleryView.touchAcquired
869
870 Component.onCompleted: {
871@@ -90,20 +146,35 @@
872 function settle() {
873 settling = true;
874 var velocity;
875- if (horizontalVelocity < 0 || visibleArea.xPosition <= 0.05 || (horizontalVelocity == 0 && visibleArea.xPosition <= 0.25)) {
876- // FIXME: compute velocity better to ensure it reaches rest position (maybe in a constant time)
877- velocity = settleVelocity;
878+ if (flickableDirection == Flickable.HorizontalFlick) {
879+ if (horizontalVelocity < 0 || visibleArea.xPosition <= 0.05 || (horizontalVelocity == 0 && visibleArea.xPosition <= 0.25)) {
880+ // FIXME: compute velocity better to ensure it reaches rest position (maybe in a constant time)
881+ velocity = settleVelocity;
882+ } else {
883+ velocity = -settleVelocity;
884+ }
885+ flick(velocity, 0);
886 } else {
887- velocity = -settleVelocity;
888+ if (verticalVelocity < 0 || visibleArea.yPosition <= 0.05 || (verticalVelocity == 0 && visibleArea.yPosition <= 0.25)) {
889+ // FIXME: compute velocity better to ensure it reaches rest position (maybe in a constant time)
890+ velocity = settleVelocity;
891+ } else {
892+ velocity = -settleVelocity;
893+ }
894+ flick(0, velocity);
895 }
896-
897- flick(velocity, 0);
898 }
899
900 function switchToViewFinder() {
901 cancelFlick();
902 switching = true;
903- flick(settleVelocity, 0);
904+ if (state == "PORTRAIT") {
905+ flick(settleVelocity, 0);
906+ } else if (state == "LANDSCAPE") {
907+ flick(0, settleVelocity);
908+ } else if (state == "INVERTED_LANDSCAPE") {
909+ flick(0, -settleVelocity);
910+ }
911 }
912
913 onMovementEnded: {
914@@ -139,39 +210,51 @@
915 }
916 }
917
918- Row {
919- id: viewsRow
920- anchors {
921- top: parent.top
922- bottom: parent.bottom
923- }
924- spacing: units.gu(1)
925-
926- ViewFinderView {
927- id: viewFinderView
928- width: viewSwitcher.width
929- height: viewSwitcher.height
930- overlayVisible: !viewSwitcher.moving && !viewSwitcher.flicking
931- inView: !viewSwitcher.atXEnd
932- opacity: inView ? 1.0 : 0.0
933- onPhotoTaken: {
934- galleryView.prependMediaToModel(filePath);
935- galleryView.showLastPhotoTaken();
936- }
937- onVideoShot: {
938- galleryView.prependMediaToModel(filePath);
939- galleryView.showLastPhotoTaken();
940- }
941- }
942-
943- GalleryViewLoader {
944- id: galleryView
945- width: viewSwitcher.width
946- height: viewSwitcher.height
947- inView: !viewSwitcher.atXBeginning
948- onExit: viewSwitcher.switchToViewFinder()
949- opacity: inView ? 1.0 : 0.0
950- }
951+ onFlickingVerticallyChanged: {
952+ // use flickingHorizontallyChanged instead of flickEnded because flickEnded
953+ // is not called when a flick is interrupted by the user
954+ if (!flickingVertically) {
955+ if (settling) {
956+ settling = false;
957+ }
958+ if (switching) {
959+ switching = true;
960+ }
961+ }
962+ }
963+
964+ onVerticalVelocityChanged: {
965+ // FIXME: this is a workaround for the lack of notification when
966+ // the user manually interrupts a flick by pressing and releasing
967+ if (verticalVelocity == 0 && !atYBeginning && !atYEnd && !settling && !moving) {
968+ settle();
969+ }
970+ }
971+
972+ ViewFinderView {
973+ id: viewFinderView
974+ width: viewSwitcher.width
975+ height: viewSwitcher.height
976+ overlayVisible: !viewSwitcher.moving && !viewSwitcher.flicking
977+ inView: viewSwitcher.ratio < 0.5
978+ opacity: inView ? 1.0 : 0.0
979+ onPhotoTaken: {
980+ galleryView.prependMediaToModel(filePath);
981+ galleryView.showLastPhotoTaken();
982+ }
983+ onVideoShot: {
984+ galleryView.prependMediaToModel(filePath);
985+ galleryView.showLastPhotoTaken();
986+ }
987+ }
988+
989+ GalleryViewLoader {
990+ id: galleryView
991+ width: viewSwitcher.width
992+ height: viewSwitcher.height
993+ inView: viewSwitcher.ratio > 0.0
994+ onExit: viewSwitcher.switchToViewFinder()
995+ opacity: inView ? 1.0 : 0.0
996 }
997 }
998

Subscribers

People subscribed via source and target branches