2 * Copyright (C) 2015-2017 Canonical, Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18import QtQuick.Layouts 1.1
21import Ubuntu.Components 1.3
22import Ubuntu.Components.ListItems 1.3 as ListItem
23import Unity.Application 0.1
25import LightDMController 0.1
26import LightDM.FullLightDM 0.1 as LightDM
28import Unity.InputInfo 0.1
32import "../../qml/Components"
33import "../../qml/Components/PanelState"
39 width: units.gu(160) + controls.width
43 target: LightDMController
49 id: applicationArguments
50 property string deviceName: "mako"
51 property string mode: "full-greeter"
55 id: mockOrientationLock
56 property int savedOrientation
61 schema.id: "com.canonical.Unity8"
63 usageModeSelector.selectedIndex = usageModeSelector.model.indexOf(usageMode)
69 schema.id: "com.canonical.keyboard.maliit"
74 deviceFilter: InputInfo.Mouse
78 deviceFilter: InputInfo.TouchPad
82 deviceFilter: InputInfo.Keyboard
85 property int physicalOrientation0
86 property int physicalOrientation90
87 property int physicalOrientation180
88 property int physicalOrientation270
89 property real primaryOrientationAngle
91 state: applicationArguments.deviceName
96 target: orientedShellLoader
102 physicalOrientation0: Qt.PortraitOrientation
103 physicalOrientation90: Qt.InvertedLandscapeOrientation
104 physicalOrientation180: Qt.InvertedPortraitOrientation
105 physicalOrientation270: Qt.LandscapeOrientation
106 primaryOrientationAngle: 0
112 target: orientedShellLoader
118 physicalOrientation90: Qt.PortraitOrientation
119 physicalOrientation180: Qt.InvertedLandscapeOrientation
120 physicalOrientation270: Qt.InvertedPortraitOrientation
121 physicalOrientation0: Qt.LandscapeOrientation
122 primaryOrientationAngle: 0
128 target: orientedShellLoader
130 height: units.gu(100)
134 physicalOrientation270: Qt.PortraitOrientation
135 physicalOrientation0: Qt.InvertedLandscapeOrientation
136 physicalOrientation90: Qt.InvertedPortraitOrientation
137 physicalOrientation180: Qt.LandscapeOrientation
138 primaryOrientationAngle: 90
144 target: orientedShellLoader
150 physicalOrientation270: Qt.InvertedPortraitOrientation
151 physicalOrientation0: Qt.LandscapeOrientation
152 physicalOrientation90: Qt.PortraitOrientation
153 physicalOrientation180: Qt.InvertedLandscapeOrientation
154 primaryOrientationAngle: 0
160 id: orientedShellLoader
162 x: ((root.width - controls.width) - width) / 2
163 y: (root.height - height) / 2
167 property bool itemDestroyed: false
168 sourceComponent: Component {
171 physicalOrientation: root.physicalOrientation0
172 orientationLocked: orientationLockedCheckBox.checked
173 orientationLock: mockOrientationLock
174 Component.onDestruction: {
175 orientedShellLoader.itemDestroyed = true;
181 function orientationsToStr(orientations) {
182 if (orientations === Qt.PrimaryOrientation) {
186 if (orientations & Qt.PortraitOrientation) {
189 if (orientations & Qt.InvertedPortraitOrientation) {
190 str += " InvertedPortrait";
192 if (orientations & Qt.LandscapeOrientation) {
195 if (orientations & Qt.InvertedLandscapeOrientation) {
196 str += " InvertedLandscape";
203 width: controls.width
206 bottom: parent.bottom
216 bottom: parent.bottom
220 boundsBehavior: Flickable.StopAtBounds
221 contentHeight: controlsColumn.height
225 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
229 activeFocusOnPress: false
231 if (orientedShellLoader.status !== Loader.Ready)
234 LightDM.Greeter.showGreeter();
238 text: "Physical Orientation:"
242 text: root.orientationsToStr(root.physicalOrientation0) + " (0)"
243 activeFocusOnPress: false
245 orientedShellLoader.item.physicalOrientation = root.physicalOrientation0;
247 color: orientedShellLoader.item && orientedShellLoader.item.physicalOrientation === root.physicalOrientation0 ?
249 __styleInstance.defaultColor
253 text: root.orientationsToStr(root.physicalOrientation90) + " (90)"
254 activeFocusOnPress: false
256 orientedShellLoader.item.physicalOrientation = root.physicalOrientation90;
258 color: orientedShellLoader.item && orientedShellLoader.item.physicalOrientation === root.physicalOrientation90 ?
260 __styleInstance.defaultColor
264 text: root.orientationsToStr(root.physicalOrientation180) + " (180)"
265 activeFocusOnPress: false
267 orientedShellLoader.item.physicalOrientation = root.physicalOrientation180;
269 color: orientedShellLoader.item && orientedShellLoader.item.physicalOrientation === root.physicalOrientation180 ?
271 __styleInstance.defaultColor
275 text: root.orientationsToStr(root.physicalOrientation270) + " (270)"
276 activeFocusOnPress: false
278 orientedShellLoader.item.physicalOrientation = root.physicalOrientation270;
280 color: orientedShellLoader.item && orientedShellLoader.item.physicalOrientation === root.physicalOrientation270 ?
282 __styleInstance.defaultColor
285 Layout.fillWidth: true
287 id: orientationLockedCheckBox
289 activeFocusOnPress: false
292 text: "Orientation Locked"
293 anchors.verticalCenter: parent.verticalCenter
298 activeFocusOnPress: false
299 onClicked: { testCase.showPowerDialog(); }
301 ListItem.ItemSelector {
302 id: deviceNameSelector
303 anchors { left: parent.left; right: parent.right }
304 activeFocusOnPress: false
306 model: ["mako", "manta", "flo", "desktop"]
307 onSelectedIndexChanged: {
309 applicationArguments.deviceName = model[selectedIndex];
310 orientedShellLoader.active = true;
313 ListItem.ItemSelector {
314 id: usageModeSelector
315 anchors { left: parent.left; right: parent.right }
316 activeFocusOnPress: false
318 model: ["Staged", "Windowed", "Automatic"]
319 function selectStaged() {selectedIndex = 0;}
320 function selectWindowed() {selectedIndex = 1;}
321 function selectAutomatic() {selectedIndex = 2;}
322 onSelectedIndexChanged: {
323 GSettingsController.setUsageMode(usageModeSelector.model[usageModeSelector.selectedIndex]);
326 MouseTouchEmulationCheckbox {
331 text: "Switch fullscreen"
332 activeFocusOnPress: false
334 var app = ApplicationManager.findApplication(ApplicationManager.focusedApplicationId);
335 app.fullscreen = !app.fullscreen;
339 Layout.fillWidth: true
342 activeFocusOnPress: false
344 var surface = SurfaceManager.inputMethodSurface;
346 surface.setState(Mir.RestoredState);
348 surface.setState(Mir.MinimizedState);
354 anchors.verticalCenter: parent.verticalCenter
358 text: Powerd.status === Powerd.On ? "Display ON" : "Display OFF"
359 activeFocusOnPress: false
361 if (Powerd.status === Powerd.On) {
362 Powerd.setStatus(Powerd.Off, Powerd.Unknown);
364 Powerd.setStatus(Powerd.On, Powerd.Unknown);
372 activeFocusOnPress: false
374 MockInputDeviceBackend.addMockDevice("/mouse" + miceModel.count, InputInfo.Mouse)
379 activeFocusOnPress: false
381 MockInputDeviceBackend.removeDevice("/mouse" + (miceModel.count - 1))
388 activeFocusOnPress: false
390 MockInputDeviceBackend.addMockDevice("/touchpad" + touchpadModel.count, InputInfo.TouchPad)
394 text: "Remove touchpad"
395 activeFocusOnPress: false
397 MockInputDeviceBackend.removeDevice("/touchpad" + (touchpadModel.count - 1))
405 activeFocusOnPress: false
407 MockInputDeviceBackend.addMockDevice("/kbd" + keyboardsModel.count, InputInfo.Keyboard)
411 activeFocusOnPress: false
414 MockInputDeviceBackend.removeDevice("/kbd" + (keyboardsModel.count - 1))
419 // Simulates what happens when the shell is moved to an external monitor and back
421 id: moveToFromMonitorButton
422 text: applicationArguments.deviceName === "desktop" ? "Move to " + prevDevName + " screen" : "Move to desktop screen"
423 activeFocusOnPress: false
424 property string prevDevName: "mako"
426 usageModeSelector.selectAutomatic();
428 if (applicationArguments.deviceName === "desktop") {
429 applicationArguments.deviceName = prevDevName;
431 prevDevName = applicationArguments.deviceName;
432 applicationArguments.deviceName = "desktop"
437 SurfaceManagerControls { }
443 name: "OrientedShell"
446 property Item orientedShell: orientedShellLoader.status === Loader.Ready ? orientedShellLoader.item : null
448 property QtObject topLevelSurfaceList
450 SignalSpy { id: signalSpy }
451 SignalSpy { id: signalSpy2 }
454 id: appRepeaterConnections
455 ignoreUnknownSignals : true
456 property var itemAddedCallback: null
458 if (itemAddedCallback) {
459 itemAddedCallback(item);
465 if (orientedShellLoader.active) {
466 // happens for the very first test function as shell
467 // is loaded by default
472 function tearDown() {
473 orientedShellLoader.itemDestroyed = false;
474 orientedShellLoader.active = false;
476 tryCompare(orientedShellLoader, "status", Loader.Null);
477 tryCompare(orientedShellLoader, "item", null);
478 // Loader.status might be Loader.Null and Loader.item might be null but the Loader
479 // actually took place. Likely because Loader waits until the next event loop
480 // iteration to do its work. So to ensure the reload, we will wait until the
481 // Shell instance gets destroyed.
482 tryCompare(orientedShellLoader, "itemDestroyed", true);
484 // kill all (fake) running apps
487 while (miceModel.count > 0)
488 MockInputDeviceBackend.removeDevice("/mouse" + (miceModel.count - 1));
489 while (touchpadModel.count > 0)
490 MockInputDeviceBackend.removeDevice("/touchpad" + (touchpadModel.count - 1));
491 while (keyboardsModel.count > 0)
492 MockInputDeviceBackend.removeDevice("/kbd" + (keyboardsModel.count - 1))
494 appRepeaterConnections.target = null;
495 appRepeaterConnections.itemAddedCallback = null;
496 signalSpy.target = null;
497 signalSpy.signalName = "";
499 LightDM.Greeter.authenticate(""); // reset greeter
501 usageModeSelector.selectStaged();
505 tryCompare(shell, "waitingOnGreeter", false, 10000); // make sure greeter didn't leave us in disabled state
507 topLevelSurfaceList = null;
512 function test_appSupportingOnlyPrimaryOrientationMakesPhoneShellStayPut() {
515 var primarySurfaceId = topLevelSurfaceList.nextId;
516 var primaryApp = ApplicationManager.startApplication("primary-oriented-app");
518 waitUntilAppWindowIsFullyLoaded(primarySurfaceId);
520 var primaryAppWindow = findAppWindowForSurfaceId(primarySurfaceId);
521 verify(primaryAppWindow)
522 var primaryDelegate = findChild(shell, "appDelegate_" + primarySurfaceId);
524 compare(primaryDelegate.focus, true);
525 compare(primaryApp.rotatesWindowContents, false);
526 compare(primaryApp.supportedOrientations, Qt.PrimaryOrientation);
528 tryCompareFunction(function(){return primaryDelegate.surface != null;}, true);
529 verify(checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle));
531 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
534 verify(checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle));
535 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
539 verify(checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle));
540 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
544 verify(checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle));
545 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
548 function test_appSupportingOnlyPrimaryOrientationWillOnlyRotateInLandscape_data() {
550 {tag: "manta", deviceName: "manta"},
551 {tag: "flo", deviceName: "flo"}
554 function test_appSupportingOnlyPrimaryOrientationWillOnlyRotateInLandscape(data) {
555 loadShell(data.deviceName);
557 var primarySurfaceId = topLevelSurfaceList.nextId;
558 var primaryApp = ApplicationManager.startApplication("primary-oriented-app");
560 waitUntilAppWindowIsFullyLoaded(primarySurfaceId);
562 var primaryAppWindow = findAppWindowForSurfaceId(primarySurfaceId);
563 verify(primaryAppWindow)
565 // primary-oriented-app supports only primary orientation
567 compare(ApplicationManager.focusedApplicationId, "primary-oriented-app");
568 compare(primaryApp.rotatesWindowContents, false);
569 compare(primaryApp.supportedOrientations, Qt.PrimaryOrientation);
570 var primaryDelegate = findChild(shell, "appDelegate_" + primarySurfaceId);
571 compare(primaryDelegate.stage, ApplicationInfoInterface.MainStage);
573 tryCompareFunction(function(){return primaryApp.surfaceList.count > 0;}, true);
575 tryCompareFunction(function(){return checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle)}, true);
576 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
580 tryCompareFunction(function(){return checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle)}, true);
581 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
585 tryCompareFunction(function(){return checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle + 180)}, true);
586 compare(shell.transformRotationAngle, root.primaryOrientationAngle + 180);
590 tryCompareFunction(function(){return checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle + 180)}, true);
591 compare(shell.transformRotationAngle, root.primaryOrientationAngle + 180);
594 function test_greeterRemainsInPrimaryOrientation_data() {
596 {tag: "mako", deviceName: "mako"},
597 {tag: "manta", deviceName: "manta"},
598 {tag: "flo", deviceName: "flo"}
601 function test_greeterRemainsInPrimaryOrientation(data) {
602 loadShell(data.deviceName);
604 var gmailApp = ApplicationManager.startApplication("gmail-webapp");
607 // ensure the mock gmail-webapp is as we expect
608 compare(gmailApp.rotatesWindowContents, false);
609 compare(gmailApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
610 | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
612 // wait until it's able to rotate
613 tryCompare(shell, "orientationChangesEnabled", true);
615 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
617 tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle + 90);
621 tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle);
623 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
625 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
627 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
630 function test_appRotatesWindowContents_data() {
632 {tag: "mako", deviceName: "mako", orientationAngleAfterRotation: 90},
633 {tag: "mako_windowed", deviceName: "mako", orientationAngleAfterRotation: 90, windowed: true},
634 {tag: "manta", deviceName: "manta", orientationAngleAfterRotation: 90},
635 {tag: "manta_windowed", deviceName: "manta", orientationAngleAfterRotation: 90, windowed: true},
636 {tag: "flo", deviceName: "flo", orientationAngleAfterRotation: 180},
637 {tag: "flo_windowed", deviceName: "flo", orientationAngleAfterRotation: 180, windowed: true}
640 function test_appRotatesWindowContents(data) {
641 loadShell(data.deviceName);
644 usageModeSelector.selectWindowed();
646 usageModeSelector.selectStaged();
649 var cameraSurfaceId = topLevelSurfaceList.nextId;
650 var cameraApp = ApplicationManager.startApplication("camera-app");
653 // ensure the mock camera-app is as we expect
654 compare(cameraApp.fullscreen, true);
655 compare(cameraApp.rotatesWindowContents, true);
656 compare(cameraApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
657 | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
659 waitUntilAppWindowIsFullyLoaded(cameraSurfaceId);
661 var cameraSurface = cameraApp.surfaceList.get(0);
662 verify(cameraSurface);
664 var focusChangedSpy = signalSpy;
665 focusChangedSpy.clear();
666 focusChangedSpy.target = cameraSurface;
667 focusChangedSpy.signalName = "activeFocusChanged";
668 verify(focusChangedSpy.valid);
670 verify(cameraSurface.activeFocus);
672 tryCompare(shell, "orientationChangesEnabled", true);
674 var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
675 verify(rotationStates);
676 var immediateTransition = null
677 for (var i = 0; i < rotationStates.transitions.length && !immediateTransition; ++i) {
678 var transition = rotationStates.transitions[i];
679 if (transition.objectName == "immediateTransition") {
680 immediateTransition = transition;
683 verify(immediateTransition);
684 var transitionSpy = signalSpy2;
685 transitionSpy.clear();
686 transitionSpy.target = immediateTransition;
687 transitionSpy.signalName = "runningChanged";
688 verify(transitionSpy.valid);
692 tryCompare(cameraSurface, "orientationAngle", data.orientationAngleAfterRotation);
694 // the rotation should have been immediate
695 // false -> true -> false
696 compare(transitionSpy.count, 2);
698 if (!data.windowed) { // subject to shell-chrome policies
699 // It should retain native dimensions regardless of its rotation/orientation
700 compare(cameraSurface.width, orientedShell.width);
701 compare(cameraSurface.height, orientedShell.height);
704 // Surface focus shouldn't have been touched because of the rotation
705 compare(focusChangedSpy.count, 0);
710 Shell orientation angle matches the screen one.
713 User switches to an app that has an orientation angle different from the
714 shell one but that also happens to support the current shell orientation
718 The app should get rotated to match shell's orientation angle
720 function test_switchingToAppWithDifferentRotation_data() {
722 {tag: "mako", deviceName: "mako", shellAngleAfterRotation: 90},
723 {tag: "manta", deviceName: "manta", shellAngleAfterRotation: 90},
724 {tag: "flo", deviceName: "flo", shellAngleAfterRotation: 180}
727 function test_switchingToAppWithDifferentRotation(data) {
728 loadShell(data.deviceName);
729 var gmailSurfaceId = topLevelSurfaceList.nextId;
730 var gmailApp = ApplicationManager.startApplication("gmail-webapp");
733 // ensure the mock gmail-webapp is as we expect
734 compare(gmailApp.rotatesWindowContents, false);
735 compare(gmailApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
736 | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
738 waitUntilAppWindowIsFullyLoaded(gmailSurfaceId);
740 var musicSurfaceId = topLevelSurfaceList.nextId;
741 var musicApp = ApplicationManager.startApplication("music-app");
744 // ensure the mock music-app is as we expect
745 compare(musicApp.rotatesWindowContents, false);
746 compare(musicApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
747 | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
748 if (data.deviceName === "manta" || data.deviceName === "flo") {
749 var musicDelegate = findChild(shell, "appDelegate_" + musicSurfaceId);
750 compare(musicDelegate.stage, ApplicationInfoInterface.MainStage);
753 waitUntilAppWindowIsFullyLoaded(musicSurfaceId);
754 tryCompare(shell, "orientationChangesEnabled", true);
757 tryCompare(shell, "transformRotationAngle", data.shellAngleAfterRotation);
759 performEdgeSwipeToSwitchToPreviousApp();
761 tryCompare(shell, "mainAppWindowOrientationAngle", data.shellAngleAfterRotation);
762 compare(shell.transformRotationAngle, data.shellAngleAfterRotation);
767 - Device supports portrait, landscape and inverted-landscape
770 1 - Launch app that supports all orientations
771 2 - Rotate device to inverted-landscape
772 3 - See that shell gets rotated to inverted-landscape accordingly
773 4 - Rotate device to inverted-portrait
776 Shell stays at inverted-landscape
779 Shell rotates to landscape
782 Rationale being that shell should be rotated to the closest supported orientation.
783 In that case, landscape and inverted-landscape are both 90 degrees away from the physical
784 orientation (Screen.orientation), so they are both equally good alternatives
786 function test_rotateToUnsupportedDeviceOrientation(data) {
788 var twitterSurfaceId = topLevelSurfaceList.nextId;
789 var twitterApp = ApplicationManager.startApplication("twitter-webapp");
792 // ensure the mock twitter-webapp is as we expect
793 compare(twitterApp.rotatesWindowContents, false);
794 compare(twitterApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
795 | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
797 waitUntilAppWindowIsFullyLoaded(twitterSurfaceId);
799 rotateTo(data.rotationAngle);
800 tryCompare(shell, "transformRotationAngle", data.rotationAngle);
803 tryCompare(shell, "transformRotationAngle", data.rotationAngle);
805 function test_rotateToUnsupportedDeviceOrientation_data() {
807 {tag: "90", rotationAngle: 90},
808 {tag: "270", rotationAngle: 270}
812 function test_launchLandscapeOnlyAppFromPortrait() {
814 var weatherSurfaceId = topLevelSurfaceList.nextId;
815 var weatherApp = ApplicationManager.startApplication("ubuntu-weather-app");
818 // ensure the mock app is as we expect
819 compare(weatherApp.supportedOrientations, Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation);
821 waitUntilAppWindowIsFullyLoaded(weatherSurfaceId);
823 var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
824 waitUntilTransitionsEnd(rotationStates);
826 tryCompareFunction(function (){return shell.transformRotationAngle === 90
827 || shell.transformRotationAngle === 270;}, true);
831 - launch an app that only supports the primary orientation
832 - launch an app that supports all orientations, such as twitter-webapp
833 - wait a bit until that app is considered to have finished initializing and is thus
834 ready to get resized/rotated
835 - switch back to previous app (only supporting primary
836 - rotate device to 90 degrees
837 - Physical orientation is 90 but Shell orientation is kept at 0 because the app
838 doesn't support such orientation
839 - do a long right-edge drag to show the apps spread
840 - tap on twitter-webapp
842 Shell will rotate to match the physical orientation.
844 This is a kind of tricky case as there are a few things happening at the same time:
845 1 - Stage switching from apps spread (phase 2) to showing the focused app (phase 0)
846 2 - orientation and aspect ratio (ie size) changes
848 This may trigger some corner case bugs. such as one were
849 the greeter is not kept completely outside the shell, causing the black rect in Shell.qml
852 function test_greeterStaysAwayAfterRotation() {
855 // Load an app which only supports primary
856 var primarySurfaceId = topLevelSurfaceList.nextId;
857 var primaryApp = ApplicationManager.startApplication("primary-oriented-app");
859 waitUntilAppWindowIsFullyLoaded(primarySurfaceId);
861 var twitterSurfaceId = topLevelSurfaceList.nextId;
862 var twitterApp = ApplicationManager.startApplication("twitter-webapp");
865 // ensure the mock twitter-webapp is as we expect
866 compare(twitterApp.rotatesWindowContents, false);
867 compare(twitterApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
868 | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
870 waitUntilAppWindowIsFullyLoaded(twitterSurfaceId);
871 waitUntilAppWindowCanRotate(twitterSurfaceId);
873 // go back to primary-oriented-app
874 performEdgeSwipeToSwitchToPreviousApp();
877 wait(1); // spin the event loop to let all bindings do their thing
878 tryCompare(shell, "transformRotationAngle", 0);
880 performEdgeSwipeToShowAppSpread();
882 // wait until things have settled
883 var greeter = findChild(shell, "greeter");
884 tryCompare(greeter, "animating", false);
886 var twitterDelegate = findChild(shell, "appDelegate_" + topLevelSurfaceList.idAt(1));
887 compare(twitterDelegate.application.appId, "twitter-webapp");
888 tap(twitterDelegate, 1, 1);
890 // now it should finally follow the physical orientation
891 tryCompare(shell, "transformRotationAngle", 90);
893 // greeter should remaing completely hidden
894 tryCompare(greeter, "shown", false);
897 function test_appInSideStageDoesntRotateOnStartUp_data() {
899 {tag: "manta", deviceName: "manta"},
900 {tag: "flo", deviceName: "flo"}
903 function test_appInSideStageDoesntRotateOnStartUp(data) {
904 WindowStateStorage.saveStage("twitter-webapp", ApplicationInfoInterface.SideStage)
905 loadShell(data.deviceName);
907 var twitterSurfaceId = topLevelSurfaceList.nextId;
908 var twitterApp = ApplicationManager.startApplication("twitter-webapp");
910 var twitterDelegate = findChild(shell, "appDelegate_" + twitterSurfaceId);
911 compare(twitterDelegate.stage, ApplicationInfoInterface.SideStage);
913 // ensure the mock twitter-webapp is as we expect
914 compare(twitterApp.rotatesWindowContents, false);
915 compare(twitterApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
916 | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
918 tryCompare(twitterDelegate, "orientationChangesEnabled", true);
920 var decoratedWindow = findChild(twitterDelegate, "decoratedWindow");
921 verify(decoratedWindow);
922 tryCompare(decoratedWindow, "counterRotate", false);
924 // no reason for any rotation animation to have taken place
925 compare(signalSpy.count, 0);
928 function test_portraitOnlyAppInSideStage_data() {
930 {tag: "manta", deviceName: "manta"},
931 {tag: "flo", deviceName: "flo"}
934 function test_portraitOnlyAppInSideStage(data) {
935 loadShell(data.deviceName);
937 var dialerDelegate = null;
938 verify(appRepeaterConnections.target);
939 appRepeaterConnections.itemAddedCallback = function(item) {
940 dialerDelegate = item;
941 verify(item.application.appId, "dialer-app");
944 WindowStateStorage.saveStage("dialer-app", ApplicationInfoInterface.SideStage)
945 var dialerApp = ApplicationManager.startApplication("dialer-app");
948 // ensure the mock dialer-app is as we expect
949 compare(dialerApp.rotatesWindowContents, false);
950 compare(dialerApp.supportedOrientations, Qt.PortraitOrientation | Qt.InvertedPortraitOrientation);
952 tryCompareFunction(function(){ return dialerDelegate != null; }, true);
953 tryCompare(dialerDelegate, "orientationChangesEnabled", true);
955 var decoratedWindow = findChild(dialerDelegate, "decoratedWindow");
956 verify(decoratedWindow);
957 tryCompare(decoratedWindow, "counterRotate", false);
959 // app must have portrait aspect ratio
960 verify(decoratedWindow.width < decoratedWindow.height);
962 // shell should remain in its primery orientation as the app in the main stage
963 // is the one that dictates its orientation. In this case it's unity8-dash
964 // which supports only primary orientation
965 compare(shell.orientation, orientedShell.orientations.primary);
968 function test_launchedAppHasActiveFocus_data() {
970 {tag: "mako", deviceName: "mako"},
971 {tag: "manta", deviceName: "manta"},
972 {tag: "flo", deviceName: "flo"}
975 function test_launchedAppHasActiveFocus(data) {
976 loadShell(data.deviceName);
978 var gmailSurfaceId = topLevelSurfaceList.nextId;
979 var gmailApp = ApplicationManager.startApplication("gmail-webapp");
981 waitUntilAppWindowIsFullyLoaded(gmailSurfaceId);
983 var gmailSurface = gmailApp.surfaceList.get(0);
984 verify(gmailSurface);
986 tryCompare(gmailSurface, "activeFocus", true);
989 function test_launchLandscapeOnlyAppOverPortraitOnlyDashThenSwitchToDash() {
992 var dashSurfaceId = topLevelSurfaceList.nextId;
993 var dashApp = ApplicationManager.startApplication("unity8-dash");
995 waitUntilAppWindowIsFullyLoaded(dashSurfaceId);
997 // starts as portrait, as unity8-dash is portrait only
998 tryCompare(shell, "transformRotationAngle", 0);
1000 var weatherSurfaceId = topLevelSurfaceList.nextId;
1001 var weatherApp = ApplicationManager.startApplication("ubuntu-weather-app");
1004 // ensure the mock app is as we expect
1005 compare(weatherApp.supportedOrientations, Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation);
1007 waitUntilAppWindowIsFullyLoaded(weatherSurfaceId);
1009 // should have rotated to landscape
1010 tryCompareFunction(function () { return shell.transformRotationAngle == 270
1011 || shell.transformRotationAngle == 90; }, true);
1013 var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
1014 waitUntilTransitionsEnd(rotationStates);
1016 ApplicationManager.requestFocusApplication("unity8-dash");
1018 // Should be back to portrait
1019 tryCompare(shell, "transformRotationAngle", 0);
1022 function test_attachRemoveInputDevices_data() {
1024 { tag: "small screen, no devices", screenWidth: units.gu(50), mouse: false, kbd: false, expectedMode: "phone", oskExpected: true },
1025 { tag: "medium screen, no devices", screenWidth: units.gu(100), mouse: false, kbd: false, expectedMode: "phone", oskExpected: true },
1026 { tag: "big screen, no devices", screenWidth: units.gu(200), mouse: false, kbd: false, expectedMode: "phone", oskExpected: true },
1027 { tag: "small screen, mouse", screenWidth: units.gu(50), mouse: true, kbd: false, expectedMode: "phone", oskExpected: true },
1028 { tag: "medium screen, mouse", screenWidth: units.gu(100), mouse: true, kbd: false, expectedMode: "desktop", oskExpected: true },
1029 { tag: "big screen, mouse", screenWidth: units.gu(200), mouse: true, kbd: false, expectedMode: "desktop", oskExpected: true },
1030 { tag: "small screen, kbd", screenWidth: units.gu(50), mouse: false, kbd: true, expectedMode: "phone", oskExpected: false },
1031 { tag: "medium screen, kbd", screenWidth: units.gu(100), mouse: false, kbd: true, expectedMode: "phone", oskExpected: false },
1032 { tag: "big screen, kbd", screenWidth: units.gu(200), mouse: false, kbd: true, expectedMode: "phone", oskExpected: false },
1033 { tag: "small screen, mouse & kbd", screenWidth: units.gu(50), mouse: true, kbd: true, expectedMode: "phone", oskExpected: false },
1034 { tag: "medium screen, mouse & kbd", screenWidth: units.gu(100), mouse: true, kbd: true, expectedMode: "desktop", oskExpected: false },
1035 { tag: "big screen, mouse & kbd", screenWidth: units.gu(200), mouse: true, kbd: true, expectedMode: "desktop", oskExpected: false },
1039 function test_attachRemoveInputDevices(data) {
1041 MockInputDeviceBackend.removeDevice("/indicator_kbd0");
1042 var shell = findChild(orientedShell, "shell");
1043 var inputMethod = findChild(shell, "inputMethod");
1045 var oldWidth = orientedShellLoader.width;
1046 orientedShellLoader.width = data.screenWidth;
1048 tryCompare(shell, "usageScenario", "phone");
1049 tryCompare(inputMethod, "enabled", true);
1050 tryCompare(oskSettings, "disableHeight", false);
1053 MockInputDeviceBackend.addMockDevice("/kbd0", InputInfo.Keyboard);
1056 MockInputDeviceBackend.addMockDevice("/mouse0", InputInfo.Mouse);
1059 tryCompare(shell, "usageScenario", data.expectedMode);
1060 tryCompare(inputMethod, "enabled", data.oskExpected);
1061 tryCompare(oskSettings, "disableHeight", data.expectedMode == "desktop" || data.kbd);
1064 orientedShellLoader.width = oldWidth;
1067 function test_screenSizeChanges() {
1069 var shell = findChild(orientedShell, "shell");
1071 tryCompare(shell, "usageScenario", "phone");
1073 // make screen larger
1074 orientedShellLoader.width = units.gu(90);
1075 tryCompare(shell, "usageScenario", "phone");
1078 MockInputDeviceBackend.addMockDevice("/mouse0", InputInfo.Mouse);
1079 tryCompare(shell, "usageScenario", "desktop");
1081 // make the screen smaller again, it should go back to staged even though there's still a mouse around
1082 orientedShellLoader.width = units.gu(40);
1084 tryCompare(shell, "usageScenario", "phone");
1087 function test_overrideStaged() {
1090 // make sure we're big enough so that the automatism starts working
1091 var oldWidth = orientedShellLoader.width;
1092 orientedShellLoader.width = units.gu(100);
1094 // start off by plugging a mouse, we should switch to windowed
1095 MockInputDeviceBackend.addMockDevice("/mouse0", InputInfo.Mouse);
1096 tryCompare(shell, "usageScenario", "desktop");
1098 // Use the toggle to go back to Staged
1099 usageModeSelector.selectStaged();
1100 tryCompare(shell, "usageScenario", "phone");
1102 // attach a second mouse, we should switch again
1103 MockInputDeviceBackend.addMockDevice("/mouse1", InputInfo.Mouse);
1104 tryCompare(shell, "usageScenario", "desktop");
1106 // Remove one mouse again, stay in windowed as there is another
1107 MockInputDeviceBackend.removeDevice("/mouse1");
1108 tryCompare(shell, "usageScenario", "desktop");
1110 // use the toggle again
1111 usageModeSelector.selectStaged();
1112 tryCompare(shell, "usageScenario", "phone");
1114 // Remove the other mouse again, stay in staged
1115 MockInputDeviceBackend.removeDevice("/mouse0");
1116 tryCompare(shell, "usageScenario", "phone");
1119 orientedShellLoader.width = oldWidth;
1122 function test_setsUsageModeOnStartup() {
1123 // Prepare inconsistent beginning (mouse & staged mode)
1124 MockInputDeviceBackend.addMockDevice("/mouse0", InputInfo.Mouse);
1125 usageModeSelector.selectStaged();
1126 compare(unity8Settings.usageMode, "Staged");
1128 // Load shell, and have it pick desktop
1129 loadShell("desktop");
1130 compare(shell.usageScenario, "desktop");
1131 compare(unity8Settings.usageMode, "Windowed");
1134 function test_overrideWindowed() {
1137 // make sure we're big enough so that the automatism starts working
1138 var oldWidth = orientedShellLoader.width;
1139 orientedShellLoader.width = units.gu(100);
1141 // No mouse attached... we should be in staged
1142 tryCompare(shell, "usageScenario", "phone");
1144 // use the toggle to go to windowed
1145 usageModeSelector.selectWindowed();
1146 tryCompare(shell, "usageScenario", "desktop");
1148 // Connect a mouse, stay in windowed
1149 MockInputDeviceBackend.addMockDevice("/mouse0", InputInfo.Mouse);
1150 tryCompare(shell, "usageScenario", "desktop");
1152 // Remove the mouse again, we should go to staged
1153 MockInputDeviceBackend.removeDevice("/mouse0");
1154 tryCompare(shell, "usageScenario", "phone");
1157 orientedShellLoader.width = oldWidth;
1161 Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1471609
1164 - Open an app which can rotate
1165 - Rotate the phone to landscape
1166 - Open the app spread
1167 - Press the power button while the app spread is open
1168 - Wait a bit and press power button again
1171 You see greeter in portrat (ie, primary orientation)
1174 You see greeter in landscape
1177 Greeter supports only the primary orientation (portrait in phones) but
1178 the stage doesn't allow orientation changes while the apps spread is open,
1181 function test_phoneWithSpreadInLandscapeWhenGreeterShowsUp() {
1184 var gmailApp = ApplicationManager.startApplication("gmail-webapp");
1187 // ensure the mock gmail-webapp is as we expect
1188 compare(gmailApp.rotatesWindowContents, false);
1189 compare(gmailApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
1190 | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
1192 // wait until it's able to rotate
1193 tryCompare(shell, "orientationChangesEnabled", true);
1196 tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle + 90);
1198 performEdgeSwipeToShowAppSpread();
1202 tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle);
1206 Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1476757
1209 1- have a portrait-only app in foreground (eg primary-oriented-app)
1210 2- launch or switch to some other application
1211 3- right-edge swipe to show the apps spread
1212 4- swipe up to close the current app (the one from step 2)
1213 5- lock the phone (press the power button)
1214 6- unlock the phone (press power button again and swipe greeter away)
1215 * app from step 1 should be on foreground and focused
1219 - The portrait-only application stays put
1222 - The portrait-only application rotates freely
1224 function test_lockPhoneAfterClosingAppInSpreadThenUnlockAndRotate() {
1227 var primarySurfaceId = topLevelSurfaceList.nextId;
1228 var primaryApp = ApplicationManager.startApplication("primary-oriented-app");
1230 waitUntilAppWindowIsFullyLoaded(primarySurfaceId);
1232 var gmailSurfaceId = topLevelSurfaceList.nextId;
1233 var gmailApp = ApplicationManager.startApplication("gmail-webapp");
1236 waitUntilAppWindowIsFullyLoaded(gmailSurfaceId);
1238 performEdgeSwipeToShowAppSpread();
1240 swipeToCloseCurrentAppInSpread();
1242 // press the power key once
1243 Powerd.setStatus(Powerd.Off, Powerd.Unknown);
1244 var greeter = findChild(shell, "greeter");
1245 tryCompare(greeter, "fullyShown", true);
1247 // and a second time to turn the display back on
1248 Powerd.setStatus(Powerd.On, Powerd.Unknown);
1252 tryCompareFunction(function() { return isAppSurfaceFocused(primarySurfaceId)}, true, 10000)
1255 signalSpy.target = shell;
1256 signalSpy.signalName = "widthChanged";
1257 verify(signalSpy.valid);
1261 // shell shouldn't have change its orientation at any moment
1262 compare(signalSpy.count, 0);
1265 function test_moveToExternalMonitor() {
1268 compare(orientedShell.orientation, Qt.InvertedLandscapeOrientation);
1269 compare(shell.transformRotationAngle, 90);
1271 moveToFromMonitorButton.clicked();
1273 tryCompare(orientedShell, "orientation", Qt.LandscapeOrientation);
1274 tryCompare(shell, "transformRotationAngle" , 0);
1278 Regression test for https://launchpad.net/bugs/1515977
1281 UI in Desktop mode and landscape
1284 - Launch a portrait-only application
1287 - Shell stays in landscape
1290 - Shell would rotate to portrait as the newly-focused app doesn't support landscape
1292 function test_portraitOnlyAppInLandscapeDesktop_data() {
1294 {tag: "mako", deviceName: "mako"},
1295 {tag: "manta", deviceName: "manta"},
1296 {tag: "flo", deviceName: "flo"}
1299 function test_portraitOnlyAppInLandscapeDesktop(data) {
1300 loadShell(data.deviceName);
1303 // setup preconditions (put shell in Desktop mode and landscape)
1305 usageModeSelector.selectWindowed();
1307 orientedShell.physicalOrientation = orientedShell.orientations.landscape;
1308 waitUntilShellIsInOrientation(orientedShell.orientations.landscape);
1309 waitForRotationAnimationsToFinish();
1312 // Launch a portrait-only application
1314 var dialerSurfaceId = topLevelSurfaceList.nextId;
1315 var dialerApp = ApplicationManager.startApplication("dialer-app");
1318 // ensure the mock dialer-app is as we expect
1319 compare(dialerApp.rotatesWindowContents, false);
1320 compare(dialerApp.supportedOrientations, Qt.PortraitOrientation | Qt.InvertedPortraitOrientation);
1322 waitUntilAppWindowIsFullyLoaded(dialerSurfaceId);
1323 waitUntilAppWindowCanRotate(dialerSurfaceId);
1324 verify(isAppSurfaceFocused(dialerSurfaceId));
1327 // check outcome (shell should stay in landscape)
1329 waitForRotationAnimationsToFinish();
1330 compare(shell.orientation, orientedShell.orientations.landscape);
1333 // angle - rotation angle in degrees clockwise, relative to the primary orientation.
1334 function rotateTo(angle) {
1337 rotate0Button.clicked();
1340 rotate90Button.clicked();
1343 rotate180Button.clicked();
1346 rotate270Button.clicked();
1351 waitForRotationAnimationsToFinish();
1354 function waitForRotationAnimationsToFinish() {
1355 var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
1356 verify(rotationStates.d);
1357 verify(rotationStates.d.stateUpdateTimer);
1359 // wait for the delayed state update to take place, if any
1360 tryCompare(rotationStates.d.stateUpdateTimer, "running", false);
1362 waitUntilTransitionsEnd(rotationStates);
1365 function waitUntilAppDelegateIsFullyInit(spreadDelegate) {
1366 tryCompare(spreadDelegate, "orientationChangesEnabled", true);
1368 var decoratedWindow = findChild(spreadDelegate, "decoratedWindow");
1369 tryCompare(decoratedWindow, "counterRotate", false);
1371 var appWindowStates = findInvisibleChild(decoratedWindow, "applicationWindowStateGroup");
1372 verify(appWindowStates);
1373 tryCompare(appWindowStates, "state", "surface");
1376 function findAppWindowForSurfaceId(surfaceId) {
1377 var delegate = findChild(shell, "appDelegate_" + surfaceId);
1379 var appWindow = findChild(delegate, "appWindow");
1383 // Wait until the ApplicationWindow for the given Application object is fully loaded
1384 // (ie, the real surface has replaced the splash screen)
1385 function waitUntilAppWindowIsFullyLoaded(surfaceId) {
1386 var appWindow = findAppWindowForSurfaceId(surfaceId);
1387 var appWindowStateGroup = findInvisibleChild(appWindow, "applicationWindowStateGroup");
1388 tryCompareFunction(function() { return appWindowStateGroup.state === "surface" }, true);
1389 waitUntilTransitionsEnd(appWindowStateGroup);
1392 function waitUntilAppWindowCanRotate(surfaceId) {
1393 var appWindow = findAppWindowForSurfaceId(surfaceId);
1394 tryCompare(appWindow, "orientationChangesEnabled", true);
1397 function waitUntilShellIsInOrientation(orientation) {
1398 tryCompare(shell, "orientation", orientation);
1399 var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
1400 waitUntilTransitionsEnd(rotationStates);
1403 function performEdgeSwipeToSwitchToPreviousApp() {
1404 // swipe just enough to ensure an app switch action.
1405 // If we swipe too much we will trigger the spread mode
1406 // and we don't want that.
1407 verify(topLevelSurfaceList.count >= 2);
1408 var previousSurfaceId = topLevelSurfaceList.idAt(1);
1410 var touchStartX = shell.width - 1;
1411 var touchStartY = shell.height / 2;
1413 touchFlick(shell, touchStartX, touchStartY, touchStartX - units.gu(8), touchStartY, units.gu(4), 10);
1415 tryCompareFunction(function(){ return topLevelSurfaceList.idAt(0); }, previousSurfaceId);
1418 function performEdgeSwipeToShowAppSpread() {
1419 var touchStartY = shell.height / 2;
1421 shell.width - 1, touchStartY,
1424 var stage = findChild(shell, "stage");
1425 tryCompare(stage, "state", "spread");
1426 waitForRendering(stage);
1429 function showPowerDialog() {
1430 var dialogs = findChild(orientedShell, "dialogs");
1431 var dialogsPrivate = findInvisibleChild(dialogs, "dialogsPrivate");
1432 dialogsPrivate.showPowerDialog();
1435 function swipeAwayGreeter() {
1436 var greeter = findChild(shell, "greeter");
1437 tryCompare(greeter, "fullyShown", true);
1438 waitForRendering(greeter)
1440 var touchX = shell.width * .75;
1441 var touchY = shell.height / 2;
1442 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
1444 // wait until the animation has finished
1445 tryCompare(greeter, "shown", false);
1446 waitForRendering(greeter);
1449 function showGreeter() {
1450 LightDM.Greeter.showGreeter();
1451 // wait until the animation has finished
1452 var greeter = findChild(shell, "greeter");
1453 tryCompare(greeter, "fullyShown", true);
1456 function loadShell(deviceName) {
1457 applicationArguments.deviceName = deviceName;
1459 // reload our test subject to get it in a fresh state once again
1460 orientedShellLoader.active = true;
1462 tryCompare(orientedShellLoader, "status", Loader.Ready);
1463 removeTimeConstraintsFromSwipeAreas(orientedShellLoader.item);
1465 shell = findChild(orientedShell, "shell");
1467 topLevelSurfaceList = findInvisibleChild(shell, "topLevelSurfaceList");
1468 verify(topLevelSurfaceList);
1470 tryCompare(shell, "waitingOnGreeter", false); // reset by greeter when ready
1472 waitUntilShellIsInOrientation(root.physicalOrientation0);
1474 waitForGreeterToStabilize();
1478 var appRepeater = findChild(shell, "appRepeater");
1480 appRepeaterConnections.target = appRepeater;
1484 function waitForGreeterToStabilize() {
1485 var greeter = findChild(shell, "greeter");
1488 var loginList = findChild(greeter, "loginList");
1489 // Only present in WideView
1491 var userList = findChild(loginList, "userList");
1493 tryCompare(userList, "movingInternally", false);
1497 // expectedAngle is in orientedShell's coordinate system
1498 function checkAppSurfaceOrientation(item, app, expectedAngle) {
1499 var surface = app.surfaceList.get(0);
1501 console.warn("no surface");
1505 var surfaceItem = findSurfaceItem(item, surface);
1507 console.warn("no surfaceItem rendering app surface");
1510 var point = surfaceItem.mapToItem(orientedShell, 0, 0);
1512 print("exptectedAngle", expectedAngle, point.x, point.y)
1513 switch (expectedAngle) {
1515 return point.x === 0 && point.y === PanelState.panelHeight;
1517 return point.x === orientedShell.width - PanelState.panelHeight && point.y === 0;
1519 return point.x === orientedShell.width && point.y === orientedShell.height - PanelState.panelHeight;
1521 return point.x === PanelState.panelHeight && point.y === orientedShell.height;
1525 function findSurfaceItem(obj, surface) {
1526 var childs = new Array(0);
1528 while (childs.length > 0) {
1529 if (childs[0].objectName === "surfaceItem"
1530 && childs[0].surface !== undefined
1531 && childs[0].surface === surface) {
1534 for (var i in childs[0].children) {
1535 childs.push(childs[0].children[i])
1537 childs.splice(0, 1);
1542 function swipeToCloseCurrentAppInSpread() {
1543 var delegateToClose = findChild(shell, "appDelegate_" + topLevelSurfaceList.idAt(0));
1544 verify(delegateToClose);
1546 var appIdToClose = ApplicationManager.get(0).appId;
1547 var appCountBefore = ApplicationManager.count;
1549 // Swipe up close to its left edge, as it is the only area of it guaranteed to be exposed
1550 // in the spread. Eg: its center could be covered by some other delegate.
1551 touchFlick(delegateToClose,
1552 1, delegateToClose.height / 2,
1553 1, - delegateToClose.height / 4);
1555 // ensure it got closed
1556 tryCompare(ApplicationManager, "count", appCountBefore - 1);
1557 compare(ApplicationManager.findApplication(appIdToClose), null);
1560 function isAppSurfaceFocused(surfaceId) {
1561 var index = topLevelSurfaceList.indexForId(surfaceId);
1562 var surface = topLevelSurfaceList.surfaceAt(index);
1564 return surface.activeFocus;
1567 function test_tabCyclyingInShutdownDialog_data() {
1569 {tag: "TAB", key: Qt.Key_Tab},
1570 {tag: "DOWN", key: Qt.Key_Down}
1574 function test_tabCyclyingInShutdownDialog(data) {
1577 testCase.showPowerDialog();
1579 var dialogs = findChild(orientedShell, "dialogs");
1580 var buttons = findChildsByType(dialogs, "Button");
1582 tryCompare(buttons[0], "activeFocus", true);
1585 tryCompare(buttons[1], "activeFocus", true);
1588 tryCompare(buttons[2], "activeFocus", true);
1591 tryCompare(buttons[0], "activeFocus", true);
1593 keyClick(Qt.Key_Escape);
1595 var dialogLoader = findChild(orientedShell, "dialogLoader");
1596 tryCompare(dialogLoader, "item", null);
1599 function test_escClosesShutdownDialog() {
1602 testCase.showPowerDialog();
1604 var dialogLoader = findChild(orientedShell, "dialogLoader");
1605 tryCompareFunction(function() { return dialogLoader.item !== null }, true);
1607 keyClick(Qt.Key_Escape);
1609 tryCompare(dialogLoader, "item", null);
1612 function test_focusOnShutdownDialogClose() {
1614 usageModeSelector.selectWindowed();
1616 var surfaceId = topLevelSurfaceList.nextId;
1617 var app = ApplicationManager.startApplication("twitter-webapp");
1618 waitUntilAppWindowIsFullyLoaded(surfaceId);
1620 var primaryAppWindow = findAppWindowForSurfaceId(surfaceId);
1621 var surface = app.surfaceList.get(0);
1622 var surfaceItem = findSurfaceItem(primaryAppWindow, surface);
1624 compare(window.activeFocusItem, surfaceItem);
1626 testCase.showPowerDialog();
1628 var dialogLoader = findChild(orientedShell, "dialogLoader");
1629 tryCompareFunction(function() { return dialogLoader.item !== null }, true);
1631 keyClick(Qt.Key_Escape);
1633 tryCompare(dialogLoader, "item", null);
1634 compare(window.activeFocusItem, surfaceItem);
1636 // Do it twice, our previous solution failed the second time ^_^
1637 testCase.showPowerDialog();
1639 tryCompareFunction(function() { return dialogLoader.item !== null }, true);
1641 keyClick(Qt.Key_Escape);
1643 tryCompare(dialogLoader, "item", null);
1644 compare(window.activeFocusItem, surfaceItem);
1647 function test_tutorialDisabledWithNoTouchscreen() {
1648 loadShell("desktop");
1649 usageModeSelector.selectWindowed();
1651 MockInputDeviceBackend.addMockDevice("/touchscreen", InputInfo.TouchScreen);
1652 var tutorial = findChild(shell, "tutorial");
1653 tryCompare(tutorial, "paused", false);
1655 MockInputDeviceBackend.removeDevice("/touchscreen");
1656 tryCompare(tutorial, "paused", true);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list