on xenial help sign in
Merge all Stages into one single codebase. Apply new spread visuals.

This deletes PhoneStage, TabletStage and DesktopStage, and merges all of the functionality into Stage. Also the spread visuals have been updated by design to work with all usage modes. (LP: #1489517, #1603914, #1635800) Approved by: Lukáš Tinkl

commitba022638122964ac4d6a178ecc574d01ec9a5320 authorMichael Zanetti <michael.zanetti@canonical.com> dateMon 24 Oct 2016, 11:34 (10 years ago) parent93b9091438d306
91 files changed+4213−2249
debian/unity8.install+1−1 plugins/Ubuntu/Gestures/TouchGate.cpp+4−1 plugins/WindowManager/TopLevelSurfaceList.cpp−5 qml/CMakeLists.txt+1−1 qml/Components/EdgeBarrier.qml+2 qml/Components/FloatingFlickable.qml+3 qml/Components/InputMethod.qml+1−1 qml/Greeter/LoginAreaContainer.qml+1−1 qml/Notifications/Notification.qml+1−1 qml/Rotation/RotationStates.qml−6 qml/Shell.qml+52−165 qml/Stages/AbstractStage.qml → qml/Stage/AbstractStage.qml qml/Stages/ApplicationWindow.qml → qml/Stage/ApplicationWindow.qml+13−37 qml/Stage/DecoratedWindow.qml+237 qml/Stages/DesktopSpread.qml → qml/Stage/DesktopSpread.qml qml/Stages/DesktopSpreadDelegate.qml → qml/Stage/DesktopSpreadDelegate.qml qml/Stages/FakeMaximizeDelegate.qml → qml/Stage/FakeMaximizeDelegate.qml qml/Stages/MainViewStyle.qml → qml/Stage/MainViewStyle.qml qml/Stages/MoveHandler.qml → qml/Stage/MoveHandler.qml+2−2 qml/Stages/OrientationChangeAnimation.qml → qml/Stage/OrientationChangeAnimation.qml qml/Stages/PhoneStage.qml → qml/Stage/PhoneStage.qml qml/Stages/PromptSurfaceAnimations.qml → qml/Stage/PromptSurfaceAnimations.qml+2−3 qml/Stages/ResizeGrip.qml → qml/Stage/ResizeGrip.qml qml/Stages/SideStage.qml → qml/Stage/SideStage.qml qml/Stages/Splash.qml → qml/Stage/Splash.qml qml/Stage/Spread/BezierCurve.qml+45 qml/Stage/Spread/KeySpline.js+66 qml/Stage/Spread/MathUtils.js+95 qml/Stage/Spread/OpacityMask.qml+82 qml/Stage/Spread/Spread.qml+162 qml/Stage/Spread/SpreadDelegateInputArea.qml+183 qml/Stage/Spread/SpreadMaths.qml+78 qml/Stage/Spread/StagedRightEdgeMaths.qml+174 qml/Stage/Spread/WindowedRightEdgeMaths.qml+81 qml/Stage/Spread/cubic-bezier.js+39 qml/Stages/SpreadDelegate.qml → qml/Stage/SpreadDelegate.qml qml/Stages/SpreadMaths.qml → qml/Stage/SpreadMaths.qml qml/Stage/Stage.qml+1881 qml/Stage/StageMaths.qml+79 qml/Stages/StagedFullscreenPolicy.qml → qml/Stage/StagedFullscreenPolicy.qml qml/Stages/SurfaceContainer.qml → qml/Stage/SurfaceContainer.qml+5−52 qml/Stages/TabletSideStageTouchGesture.qml → qml/Stage/TabletSideStageTouchGesture.qml qml/Stages/TabletStage.qml → qml/Stage/TabletStage.qml qml/Stages/TopLevelSurfaceRepeater.qml → qml/Stage/TopLevelSurfaceRepeater.qml+9 qml/Stages/TransformedSpreadDelegate.qml → qml/Stage/TransformedSpreadDelegate.qml qml/Stages/TransformedTabletSpreadDelegate.qml → qml/Stage/TransformedTabletSpreadDelegate.qml qml/Stages/WindowControlsOverlay.qml → qml/Stage/WindowControlsOverlay.qml qml/Stages/WindowDecoration.qml → qml/Stage/WindowDecoration.qml+1−1 qml/Stage/WindowInfoItem.qml+53 qml/Stages/WindowResizeArea.qml → qml/Stage/WindowResizeArea.qml+32−31 qml/Stages/WindowedFullscreenPolicy.qml → qml/Stage/WindowedFullscreenPolicy.qml qml/Stages/graphics/PageHeaderBaseDividerBottom@18.png → qml/Stage/graphics/PageHeaderBaseDividerBottom@18.png qml/Stages/graphics/PageHeaderBaseDividerLight@18.png → qml/Stage/graphics/PageHeaderBaseDividerLight@18.png qml/Stages/graphics/PageHeaderBaseDividerLight@18.sci → qml/Stage/graphics/PageHeaderBaseDividerLight@18.sci qml/Stages/graphics/arrows-centre.png → qml/Stage/graphics/arrows-centre.png qml/Stages/graphics/arrows.png → qml/Stage/graphics/arrows.png qml/Stages/graphics/sidestage_drag.svg → qml/Stage/graphics/sidestage_drag.svg qml/Stages/graphics/sidestage_handle@20.png → qml/Stage/graphics/sidestage_handle@20.png qml/Stages/graphics/sidestage_handle@20.sci → qml/Stage/graphics/sidestage_handle@20.sci qml/Stages/graphics/sidestage_open.svg → qml/Stage/graphics/sidestage_open.svg qml/Stages/graphics/window-close.svg → qml/Stage/graphics/window-close.svg qml/Stages/DecoratedWindow.qml−157 qml/Stages/DesktopStage.qml−825 qml/Tutorial/TutorialRight.qml+1−1 qml/Stages/graphics/dropshadow2gu@30.png → qml/graphics/dropshadow2gu@30.png qml/Stages/graphics/dropshadow2gu@30.sci → qml/graphics/dropshadow2gu@30.sci tests/graphics/applicationIcons/dash.png → tests/graphics/applicationIcons/dash@18.png tests/mocks/Unity/Application/ApplicationInfo.cpp+2−2 tests/mocks/Unity/Application/ApplicationManager.cpp+2 tests/mocks/Unity/Application/MirSurface.cpp+1 tests/mocks/Unity/Application/MirSurfaceItem.cpp+13 tests/mocks/Unity/Application/MirSurfaceListModel.cpp−1 tests/mocks/Unity/Application/VirtualKeyboard.cpp+9−7 tests/mocks/Unity/Application/resources/MirSurfaceItem.qml+2−5 tests/qmltests/CMakeLists.txt+8−8 tests/qmltests/Stages/ApplicationCheckBox.qml → tests/qmltests/Stage/ApplicationCheckBox.qml tests/qmltests/Stages/SizeHintField.qml → tests/qmltests/Stage/SizeHintField.qml tests/qmltests/Stages/SurfaceManagerControls.qml → tests/qmltests/Stage/SurfaceManagerControls.qml tests/qmltests/Stages/SurfaceManagerField.qml → tests/qmltests/Stage/SurfaceManagerField.qml tests/qmltests/Stages/tst_ApplicationWindow.qml → tests/qmltests/Stage/tst_ApplicationWindow.qml+1−1 tests/qmltests/Stage/tst_DecoratedWindow.qml+232 tests/qmltests/Stages/tst_DesktopStage.qml → tests/qmltests/Stage/tst_DesktopStage.qml+29−71 tests/qmltests/Stages/tst_PhoneStage.qml → tests/qmltests/Stage/tst_PhoneStage.qml+149−211 tests/qmltests/Stages/tst_Splash.qml → tests/qmltests/Stage/tst_Splash.qml+1−1 tests/qmltests/Stages/tst_SpreadDelegate.qml → tests/qmltests/Stage/tst_SpreadDelegate.qml tests/qmltests/Stages/tst_SurfaceContainer.qml → tests/qmltests/Stage/tst_SurfaceContainer.qml+2−2 tests/qmltests/Stages/tst_TabletStage.qml → tests/qmltests/Stage/tst_TabletStage.qml+107−241 tests/qmltests/Stages/tst_WindowResizeArea.qml → tests/qmltests/Stage/tst_WindowResizeArea.qml+40−36 tests/qmltests/tst_OrientedShell.qml+38−198 tests/qmltests/tst_Shell.qml+182−163 tests/qmltests/tst_ShellWithPin.qml+9−11
debian/unity8.install+1−1
@@ -16,7 +16,7 @@ usr/share/unity8/DeviceConfiguration.qml
1616usr/share/unity8/OrientedShell.qml
1717usr/share/unity8/DisabledScreenNotice.qml
1818usr/share/unity8/Shell.qml
19−usr/share/unity8/Stages
19+usr/share/unity8/Stage
2020usr/share/unity8/Tutorial
2121usr/share/unity8/Wizard
2222usr/share/url-dispatcher/urls/unity8-dash.url-dispatcher
plugins/Ubuntu/Gestures/TouchGate.cpp+4−1
@@ -61,7 +61,10 @@ void TouchGate::touchEvent(QTouchEvent *event)
6161 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
6262
6363 if (touchPoint.state() == Qt::TouchPointPressed) {
64− Q_ASSERT(!m_touchInfoMap.contains(touchPoint.id()));
64+// FIXME: This assert triggers frequently in make trySomething. We have verified
65+// that it's a bug in the mouse to touch conversion of the test environment
66+// and not in the actual product. Still, it probably should be cleaned up eventually.
67+// Q_ASSERT(!m_touchInfoMap.contains(touchPoint.id()));
6568 m_touchInfoMap[touchPoint.id()].ownership = OwnershipRequested;
6669 m_touchInfoMap[touchPoint.id()].ended = false;
6770 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
plugins/WindowManager/TopLevelSurfaceList.cpp−5
@@ -133,11 +133,6 @@ void TopLevelSurfaceList::appendSurfaceHelper(MirSurfaceInterface *surface, Appl
133133
134134void TopLevelSurfaceList::connectSurface(MirSurfaceInterface *surface)
135135{
136− connect(surface, &MirSurfaceInterface::focusedChanged, this, [this, surface](bool focused){
137− if (focused) {
138− this->raise(surface);
139− }
140− });
141136 connect(surface, &MirSurfaceInterface::liveChanged, this, [this, surface](bool live){
142137 if (!live) {
143138 onSurfaceDied(surface);
qml/CMakeLists.txt+1−1
@@ -12,7 +12,7 @@ set(QML_DIRS
1212 Launcher
1313 Notifications
1414 Panel
15− Stages
15+ Stage
1616 Rotation
1717 Tutorial
1818 Wizard
qml/Components/EdgeBarrier.qml+2
@@ -29,6 +29,8 @@ Item {
2929 // Supported values are: Qt.LeftEdge, Qt.RightEdge
3030 property int edge: Qt.LeftEdge
3131
32+ readonly property alias progress: controller.progress
33+
3234 property Item target: parent
3335 function push(amount) { controller.push(amount); }
3436 signal passed()
qml/Components/FloatingFlickable.qml+3
@@ -35,6 +35,9 @@ Item {
3535 property alias contentX: flickable.contentX
3636 property alias contentY: flickable.contentY
3737 property alias direction: swipeArea.direction
38+ property alias leftMargin: flickable.leftMargin
39+ property alias rightMargin: flickable.rightMargin
40+ property alias dragging: flickable.dragging
3841
3942 MouseEventGenerator {
4043 id: mouseEventGenerator
qml/Components/InputMethod.qml+1−1
@@ -21,7 +21,7 @@ import Ubuntu.Gestures 0.1
2121Item {
2222 id: root
2323
24− readonly property rect visibleRect: surfaceItem.surface ? surfaceItem.surface.inputBounds : Qt.rect(0, 0, 0, 0)
24+ readonly property rect visibleRect: surfaceItem.surface && visible ? surfaceItem.surface.inputBounds : Qt.rect(0, 0, 0, 0)
2525
2626 MirSurfaceItem {
2727 id: surfaceItem
qml/Greeter/LoginAreaContainer.qml+1−1
@@ -26,7 +26,7 @@ Item {
2626 rightMargin: -units.gu(1.5)
2727 bottomMargin: -units.gu(1.5)
2828 }
29− source: "../Stages/graphics/dropshadow2gu.sci"
29+ source: "../graphics/dropshadow2gu.sci"
3030 opacity: 0.35
3131 }
3232
qml/Notifications/Notification.qml+1−1
@@ -143,7 +143,7 @@ StyledItem {
143143 fill: contents
144144 margins: shapedBack.visible ? -units.gu(1) : -units.gu(1.5)
145145 }
146− source: "../Stages/graphics/dropshadow2gu.sci"
146+ source: "../graphics/dropshadow2gu.sci"
147147 opacity: notification.opacity * 0.5
148148 enabled: !fullscreen
149149 }
qml/Rotation/RotationStates.qml−6
@@ -121,12 +121,6 @@ StateGroup {
121121 }
122122 }
123123
124− property var shellBeingResized: Binding {
125− target: root.shell
126− property: "beingResized"
127− value: d.transitioning
128− }
129−
130124 readonly property int fullAnimation: 0
131125 readonly property int indicatorsBarAnimation: 1
132126 readonly property int noAnimation: 2
qml/Shell.qml+52−165
@@ -35,7 +35,7 @@ import "Launcher"
3535import "Panel"
3636import "Components"
3737import "Notifications"
38−import "Stages"
38+import "Stage"
3939import "Tutorial"
4040import "Wizard"
4141import Unity.Notifications 1.0 as NotificationBackend
@@ -58,24 +58,22 @@ StyledItem {
5858 property real nativeWidth
5959 property real nativeHeight
6060 property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
61− property bool beingResized
6261 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
6362 property string mode: "full-greeter"
6463 property alias oskEnabled: inputMethod.enabled
6564 function updateFocusedAppOrientation() {
66− applicationsDisplayLoader.item.updateFocusedAppOrientation();
65+ stage.updateFocusedAppOrientation();
6766 }
6867 function updateFocusedAppOrientationAnimated() {
69− applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
68+ stage.updateFocusedAppOrientationAnimated();
7069 }
7170 property bool hasMouse: false
7271
7372 // to be read from outside
74− readonly property int mainAppWindowOrientationAngle:
75− applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindowOrientationAngle : 0
73+ readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
7674
7775 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
78− && (applicationsDisplayLoader.item && applicationsDisplayLoader.item.orientationChangesEnabled)
76+ && stage.orientationChangesEnabled
7977 && (!greeter || !greeter.animating)
8078
8179 readonly property bool showingGreeter: greeter && greeter.shown
@@ -89,19 +87,13 @@ StyledItem {
8987 return Qt.PrimaryOrientation;
9088 } else if (showingGreeter || notifications.topmostIsFullscreen) {
9189 return Qt.PrimaryOrientation;
92− } else if (applicationsDisplayLoader.item) {
93− return shell.orientations.map(applicationsDisplayLoader.item.supportedOrientations);
9490 } else {
95− // we just don't care
96− return Qt.PortraitOrientation
97− | Qt.LandscapeOrientation
98− | Qt.InvertedPortraitOrientation
99− | Qt.InvertedLandscapeOrientation;
91+ return shell.orientations.map(stage.supportedOrientations);
10092 }
10193 }
10294
103− readonly property var mainApp:
104− applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
95+ readonly property var mainApp: stage.mainApp
96+
10597 onMainAppChanged: {
10698 if (mainApp) {
10799 _onMainAppChanged(mainApp.appId);
@@ -278,154 +270,50 @@ StyledItem {
278270 applicationsModel: ApplicationManager
279271 }
280272
281− Loader {
282− id: applicationsDisplayLoader
283− objectName: "applicationsDisplayLoader"
273+ Stage {
274+ id: stage
275+ objectName: "stage"
284276 anchors.fill: parent
277+ focus: true
278+
279+ dragAreaWidth: shell.edgeSize
280+ background: wallpaperResolver.background
281+ leftEdgeDragProgress: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 :
282+ Math.max(0, (launcher.dragDistance * (stage.width - launcher.panelWidth) / stage.width) - launcher.panelWidth)
285283
286− // When we have a locked app, we only want to show that one app.
287− // FIXME: do this in a less traumatic way. We currently only allow
288− // locked apps in phone mode (see FIXME in Lockscreen component in
289− // this same file). When that changes, we need to do something
290− // nicer here. But this code is currently just to prevent a
291− // theoretical attack where user enters lockedApp mode, then makes
292− // the screen larger (maybe connects to monitor) and tries to enter
293− // tablet mode.
284+ applicationManager: ApplicationManager
285+ topLevelSurfaceList: topLevelSurfaceList
286+ inputMethodRect: inputMethod.visibleRect
294287
295288 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
296− ? "phone"
297− : shell.usageScenario
298− readonly property string qmlComponent: {
299− if (applicationsDisplayLoader.usageScenario === "phone") {
300− return "Stages/PhoneStage.qml";
301− } else if (applicationsDisplayLoader.usageScenario === "tablet") {
302− return "Stages/TabletStage.qml";
303− } else {
304− return "Stages/DesktopStage.qml";
305− }
306− }
307− // TODO: Ensure the current stage is destroyed before the new one gets loaded.
308− // Currently the new one will get loaded while the old is still hanging
309− // around for a bit, which might lead to conflicts where both stages
310− // change the model simultaneously.
311− onQmlComponentChanged: {
312− if (item) item.stageAboutToBeUnloaded();
313− source = qmlComponent;
314− }
289+ ? "phone"
290+ : shell.usageScenario
291+
292+ mode: usageScenario == "phone" ? "staged"
293+ : usageScenario == "tablet" ? "stagedWithSideStage"
294+ : "windowed"
295+
296+ shellOrientation: shell.orientation
297+ shellOrientationAngle: shell.orientationAngle
298+ orientations: shell.orientations
299+ nativeWidth: shell.nativeWidth
300+ nativeHeight: shell.nativeHeight
315301
316− property bool interactive: (!greeter || !greeter.shown)
302+ interactive: (!greeter || !greeter.shown)
317303 && panel.indicators.fullyClosed
318304 && launcher.progress == 0
319305 && !notifications.useModal
320306
321307 onInteractiveChanged: { if (interactive) { focus = true; } }
322308
323− Binding {
324− target: applicationsDisplayLoader.item
325− property: "focus"
326− value: true
327− }
328− Binding {
329− target: applicationsDisplayLoader.item
330− property: "objectName"
331− value: "stage"
332− }
333− Binding {
334− target: applicationsDisplayLoader.item
335− property: "dragAreaWidth"
336− value: shell.edgeSize
337− }
338− Binding {
339− target: applicationsDisplayLoader.item
340− property: "maximizedAppTopMargin"
341− // Not just using panel.panelHeight as that changes depending on the focused app.
342− value: panel.indicators.minimizedPanelHeight
343− }
344− Binding {
345− target: applicationsDisplayLoader.item
346− property: "interactive"
347− value: applicationsDisplayLoader.interactive
348− }
349− Binding {
350− target: applicationsDisplayLoader.item
351− property: "spreadEnabled"
352− value: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
353− }
354− Binding {
355− target: applicationsDisplayLoader.item
356− property: "inverseProgress"
357− value: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 : launcher.progress
358− }
359− Binding {
360− target: applicationsDisplayLoader.item
361− property: "shellOrientationAngle"
362− value: shell.orientationAngle
363− }
364− Binding {
365− target: applicationsDisplayLoader.item
366− property: "shellOrientation"
367− value: shell.orientation
368− }
369− Binding {
370− target: applicationsDisplayLoader.item
371− property: "orientations"
372− value: shell.orientations
373− }
374− Binding {
375− target: applicationsDisplayLoader.item
376− property: "background"
377− value: wallpaperResolver.cachedBackground
378− }
379− Binding {
380− target: applicationsDisplayLoader.item
381− property: "nativeWidth"
382− value: shell.nativeWidth
383− }
384− Binding {
385− target: applicationsDisplayLoader.item
386− property: "nativeHeight"
387− value: shell.nativeHeight
388− }
389− Binding {
390− target: applicationsDisplayLoader.item
391− property: "beingResized"
392− value: shell.beingResized
393− }
394− Binding {
395− target: applicationsDisplayLoader.item
396− property: "keepDashRunning"
397− value: launcher.shown || launcher.dashSwipe
398− }
399− Binding {
400− target: applicationsDisplayLoader.item
401− property: "suspended"
402− value: greeter.shown
403− }
404− Binding {
405− target: applicationsDisplayLoader.item
406− property: "altTabPressed"
407− value: physicalKeysMapper.altTabPressed
408− }
409− Binding {
410− target: applicationsDisplayLoader.item
411− property: "leftMargin"
412− value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
413− }
414− Binding {
415− target: applicationsDisplayLoader.item
416− property: "applicationManager"
417− value: ApplicationManager
418− }
419− Binding {
420− target: applicationsDisplayLoader.item
421− property: "topLevelSurfaceList"
422− value: topLevelSurfaceList
423− }
424− Binding {
425− target: applicationsDisplayLoader.item
426− property: "oskEnabled"
427− value: shell.oskEnabled
428− }
309+ leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
310+ suspended: greeter.shown
311+ keepDashRunning: launcher.shown || launcher.dashSwipe
312+ altTabPressed: physicalKeysMapper.altTabPressed
313+ oskEnabled: shell.oskEnabled
314+
315+ // TODO: This is not implemented yet in the new stage...
316+ spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
429317 }
430318 }
431319
@@ -442,6 +330,7 @@ StyledItem {
442330
443331 Loader {
444332 id: greeterLoader
333+ objectName: "greeterLoader"
445334 anchors.fill: parent
446335 anchors.topMargin: panel.panelHeight
447336 sourceComponent: shell.mode != "shell" ? integratedGreeter :
@@ -650,7 +539,7 @@ StyledItem {
650539 }
651540 onFocusChanged: {
652541 if (!focus) {
653− applicationsDisplayLoader.focus = true;
542+ stage.focus = true;
654543 }
655544 }
656545
@@ -706,12 +595,12 @@ StyledItem {
706595 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
707596 inputMethod.visible ||
708597 (launcher.shown && !launcher.lockedVisible) ||
709− panel.indicators.shown || stage.dragProgress > 0
598+ panel.indicators.shown || stage.rightEdgeDragProgress > 0
710599 usageScenario: shell.usageScenario
711600 lastInputTimestamp: inputFilter.lastInputTimestamp
712601 launcher: launcher
713602 panel: panel
714− stage: applicationsDisplayLoader.item
603+ stage: stage
715604 }
716605
717606 Wizard {
@@ -802,7 +691,7 @@ StyledItem {
802691 z: dialogs.z + 10
803692 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
804693 Connections {
805− target: applicationsDisplayLoader.item
694+ target: stage
806695 ignoreUnknownSignals: true
807696 onItemSnapshotRequested: itemGrabber.capture(item)
808697 }
@@ -814,7 +703,7 @@ StyledItem {
814703 z: itemGrabber.z + 1
815704 topBoundaryOffset: panel.panelHeight
816705
817− confiningItem: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.itemConfiningMouseCursor : null
706+ confiningItem: stage.itemConfiningMouseCursor
818707
819708 property bool mouseNeverMoved: true
820709 Binding {
@@ -828,10 +717,9 @@ StyledItem {
828717
829718 height: units.gu(3)
830719
831− readonly property var previewRectangle: applicationsDisplayLoader.item && applicationsDisplayLoader.item.previewRectangle &&
832− applicationsDisplayLoader.item.previewRectangle.target &&
833− applicationsDisplayLoader.item.previewRectangle.target.dragging ?
834− applicationsDisplayLoader.item.previewRectangle : null
720+ readonly property var previewRectangle: stage.previewRectangle.target &&
721+ stage.previewRectangle.target.dragging ?
722+ stage.previewRectangle : null
835723
836724 onPushedLeftBoundary: {
837725 if (buttons === Qt.NoButton) {
@@ -842,9 +730,8 @@ StyledItem {
842730 }
843731
844732 onPushedRightBoundary: {
845− if (buttons === Qt.NoButton && applicationsDisplayLoader.item
846− && applicationsDisplayLoader.item.pushRightEdge) {
847− applicationsDisplayLoader.item.pushRightEdge(amount);
733+ if (buttons === Qt.NoButton) {
734+ stage.pushRightEdge(amount);
848735 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
849736 previewRectangle.maximizeRight(amount);
850737 }
qml/Stages/AbstractStage.qml → qml/Stage/AbstractStage.qmlrenamed

The content is the same.

qml/Stages/ApplicationWindow.qml → qml/Stage/ApplicationWindow.qmlrenamed+13−37
@@ -20,8 +20,8 @@ import Unity.Application 0.1
2020
2121FocusScope {
2222 id: root
23− implicitWidth: surfaceContainer.implicitWidth
24− implicitHeight: surfaceContainer.implicitHeight
23+ implicitWidth: requestedWidth
24+ implicitHeight: requestedHeight
2525
2626 // to be read from outside
2727 property alias interactive: surfaceContainer.interactive
@@ -29,22 +29,10 @@ FocusScope {
2929 readonly property string title: surface && surface.name !== "" ? surface.name : d.name
3030 readonly property QtObject focusedSurface: d.focusedSurface.surface
3131
32− // overridable from outside
33− property bool fullscreen: {
34− if (surface) {
35− return surface.state === Mir.FullscreenState;
36− } else if (application) {
37− return application.fullscreen;
38− } else {
39− return false;
40− }
41− }
42−
4332 // to be set from outside
4433 property QtObject surface
4534 property QtObject application
4635 property int surfaceOrientationAngle
47− property alias resizeSurface: surfaceContainer.resizeSurface
4836 property int requestedWidth: -1
4937 property int requestedHeight: -1
5038 property real splashRotation: 0
@@ -156,6 +144,9 @@ FocusScope {
156144 id: screenshotImage
157145 objectName: "screenshotImage"
158146 anchors.fill: parent
147+ fillMode: Image.PreserveAspectCrop
148+ horizontalAlignment: Image.AlignLeft
149+ verticalAlignment: Image.AlignTop
159150 antialiasing: !root.interactive
160151 z: 1
161152
@@ -197,6 +188,7 @@ FocusScope {
197188
198189 SurfaceContainer {
199190 id: surfaceContainer
191+ anchors.fill: parent
200192 z: splashLoader.z + 1
201193 requestedWidth: root.requestedWidth
202194 requestedHeight: root.requestedHeight
@@ -220,6 +212,8 @@ FocusScope {
220212 surface: model.surface
221213 width: root.width
222214 height: root.height
215+ requestedWidth: root.requestedWidth
216+ requestedHeight: root.requestedHeight
223217 isPromptSurface: true
224218 z: surfaceContainer.z + (promptSurfacesRepeater.count - index)
225219 property int index: model.index
@@ -239,28 +233,6 @@ FocusScope {
239233 property Item first: null
240234 }
241235
242− // SurfaceContainer size drives ApplicationWindow size
243− Binding {
244− target: root; property: "width"
245− value: stateGroup.state === "surface" ? surfaceContainer.width : root.requestedWidth
246− when: root.requestedWidth >= 0
247− }
248− Binding {
249− target: root; property: "height"
250− value: stateGroup.state === "surface" ? surfaceContainer.height : root.requestedHeight
251− when: root.requestedHeight >= 0
252− }
253−
254− // ApplicationWindow size drives SurfaceContainer size
255− Binding {
256− target: surfaceContainer; property: "width"; value: root.width
257− when: root.requestedWidth < 0
258− }
259− Binding {
260− target: surfaceContainer; property: "height"; value: root.height
261− when: root.requestedHeight < 0
262− }
263−
264236 StateGroup {
265237 id: stateGroup
266238 objectName: "applicationWindowStateGroup"
@@ -287,6 +259,11 @@ FocusScope {
287259 (d.liveSurface ||
288260 (d.applicationState !== ApplicationInfoInterface.Running
289261 && screenshotImage.status !== Image.Ready))
262+ PropertyChanges {
263+ target: root
264+ implicitWidth: surfaceContainer.implicitWidth
265+ implicitHeight: surfaceContainer.implicitHeight
266+ }
290267 },
291268 State {
292269 name: "screenshot"
@@ -424,5 +401,4 @@ FocusScope {
424401 }
425402 ]
426403 }
427−
428404}
qml/Stage/DecoratedWindow.qmlnew file+237
@@ -0,0 +1,237 @@
1+/*
2+ * Copyright (C) 2014-2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Unity.Application 0.1
20+import "Spread/MathUtils.js" as MathUtils
21+
22+FocusScope {
23+ id: root
24+
25+ // The DecoratedWindow takes requestedWidth/requestedHeight and asks its surface to be resized to that
26+ // (minus the window decoration size in case hasDecoration and showDecoration are true)
27+ // The surface might not be able to resize to the requested values. It will return its actual size
28+ // in implicitWidth/implicitHeight.
29+
30+ property alias application: applicationWindow.application
31+ property alias surface: applicationWindow.surface
32+ readonly property alias focusedSurface: applicationWindow.focusedSurface
33+ property alias active: decoration.active
34+ readonly property alias title: applicationWindow.title
35+ property alias maximizeButtonShown: decoration.maximizeButtonShown
36+ property alias interactive: applicationWindow.interactive
37+ readonly property alias orientationChangesEnabled: applicationWindow.orientationChangesEnabled
38+
39+ // Changing this will actually add/remove a decoration, meaning, requestedHeight will take the decoration into account.
40+ property bool hasDecoration: true
41+ // This will temporarily show/hide the decoration without actually changing the surface's dimensions
42+ property real showDecoration: 1
43+ property bool animateDecoration: false
44+ property bool showHighlight: false
45+ property int highlightSize: units.gu(1)
46+ property real shadowOpacity: 0
47+ property bool darkening: false
48+
49+ property real requestedWidth
50+ property real requestedHeight
51+ property real scaleToPreviewProgress: 0
52+ property int scaleToPreviewSize: units.gu(30)
53+
54+ property alias surfaceOrientationAngle: applicationWindow.surfaceOrientationAngle
55+ readonly property real decorationHeight: Math.min(d.visibleDecorationHeight, d.requestedDecorationHeight)
56+ readonly property bool counterRotate: surfaceOrientationAngle != 0 && surfaceOrientationAngle != 180
57+
58+ readonly property int minimumWidth: !counterRotate ? applicationWindow.minimumWidth : applicationWindow.minimumHeight
59+ readonly property int minimumHeight: decorationHeight + (!counterRotate ? applicationWindow.minimumHeight : applicationWindow.minimumWidth)
60+ readonly property int maximumWidth: !counterRotate ? applicationWindow.maximumWidth : applicationWindow.maximumHeight
61+ readonly property int maximumHeight: (root.decorationShown && applicationWindow.maximumHeight > 0 ? decoration.height : 0)
62+ + (!counterRotate ? applicationWindow.maximumHeight : applicationWindow.maximumWidth)
63+ readonly property int widthIncrement: !counterRotate ? applicationWindow.widthIncrement : applicationWindow.heightIncrement
64+ readonly property int heightIncrement: !counterRotate ? applicationWindow.heightIncrement : applicationWindow.widthIncrement
65+
66+ property alias overlayShown: decoration.overlayShown
67+ property alias stageWidth: moveHandler.stageWidth
68+ property alias stageHeight: moveHandler.stageHeight
69+ readonly property alias dragging: moveHandler.dragging
70+
71+ readonly property Item clientAreaItem: applicationWindow
72+
73+ signal closeClicked()
74+ signal maximizeClicked()
75+ signal maximizeHorizontallyClicked()
76+ signal maximizeVerticallyClicked()
77+ signal minimizeClicked()
78+ signal decorationPressed()
79+ signal decorationReleased()
80+
81+ QtObject {
82+ id: d
83+ property int requestedDecorationHeight: root.hasDecoration ? decoration.height : 0
84+ Behavior on requestedDecorationHeight { enabled: root.animateDecoration; UbuntuNumberAnimation { } }
85+
86+ property int visibleDecorationHeight: root.hasDecoration ? root.showDecoration * decoration.height : 0
87+ Behavior on visibleDecorationHeight { enabled: root.animateDecoration; UbuntuNumberAnimation { } }
88+ }
89+
90+ StateGroup {
91+ states: [
92+ State {
93+ name: "normal"; when: root.scaleToPreviewProgress <= 0 && root.application.state === ApplicationInfoInterface.Running
94+ PropertyChanges {
95+ target: root
96+ implicitWidth: counterRotate ? applicationWindow.implicitHeight : applicationWindow.implicitWidth
97+ implicitHeight: root.decorationHeight + (counterRotate ? applicationWindow.implicitWidth: applicationWindow.implicitHeight)
98+ }
99+ },
100+ State {
101+ name: "normalSuspended"; when: root.scaleToPreviewProgress <= 0 && root.application.state !== ApplicationInfoInterface.Running
102+ extend: "normal"
103+ PropertyChanges {
104+ target: root
105+ implicitWidth: counterRotate ? applicationWindow.requestedHeight : applicationWindow.requestedWidth
106+ implicitHeight: root.decorationHeight + (counterRotate ? applicationWindow.requestedWidth: applicationWindow.requestedHeight)
107+ }
108+ },
109+ State {
110+ name: "preview"; when: root.scaleToPreviewProgress > 0
111+ PropertyChanges {
112+ target: root
113+ implicitWidth: MathUtils.linearAnimation(0, 1, applicationWindow.oldRequestedWidth, root.scaleToPreviewSize, root.scaleToPreviewProgress)
114+ implicitHeight: MathUtils.linearAnimation(0, 1, applicationWindow.oldRequestedHeight, root.scaleToPreviewSize, root.scaleToPreviewProgress)
115+ }
116+ PropertyChanges {
117+ target: applicationWindow;
118+ requestedWidth: applicationWindow.oldRequestedWidth
119+ requestedHeight: applicationWindow.oldRequestedHeight
120+ width: MathUtils.linearAnimation(0, 1, applicationWindow.oldRequestedWidth, applicationWindow.minSize, root.scaleToPreviewProgress)
121+ height: MathUtils.linearAnimation(0, 1, applicationWindow.oldRequestedHeight, applicationWindow.minSize, root.scaleToPreviewProgress)
122+ itemScale: root.implicitWidth / width
123+ }
124+ }
125+ ]
126+ }
127+
128+ Rectangle {
129+ id: selectionHighlight
130+ objectName: "selectionHighlight"
131+ anchors.fill: parent
132+ anchors.margins: -root.highlightSize
133+ color: "white"
134+ opacity: showHighlight ? 0.55 : 0
135+ visible: opacity > 0
136+ }
137+
138+ BorderImage {
139+ id: dropShadow
140+ anchors {
141+ left: parent.left; top: parent.top; right: parent.right
142+ margins: active ? -units.gu(2) : -units.gu(1.5)
143+ }
144+ height: Math.min(applicationWindow.implicitHeight, applicationWindow.height) * applicationWindow.itemScale
145+ + root.decorationHeight * Math.min(1, root.showDecoration) + (active ? units.gu(4) : units.gu(3))
146+ source: "../graphics/dropshadow2gu.sci"
147+ opacity: root.shadowOpacity
148+ }
149+
150+ WindowDecoration {
151+ id: decoration
152+ target: root.parent || null
153+ objectName: "appWindowDecoration"
154+ anchors { left: parent.left; top: parent.top; right: parent.right }
155+ height: units.gu(3)
156+ width: root.width
157+ title: applicationWindow.title
158+ opacity: root.hasDecoration ? Math.min(1, root.showDecoration) : 0
159+
160+ Behavior on opacity { UbuntuNumberAnimation { } }
161+
162+ onCloseClicked: root.closeClicked();
163+ onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); }
164+ onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); }
165+ onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }
166+ onMinimizeClicked: root.minimizeClicked();
167+ onPressed: root.decorationPressed();
168+
169+ onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
170+ onPositionChanged: moveHandler.handlePositionChanged(mouse)
171+ onReleased: {
172+ root.decorationReleased();
173+ moveHandler.handleReleased();
174+ }
175+ }
176+
177+ MoveHandler {
178+ id: moveHandler
179+ objectName: "moveHandler"
180+ target: root.parent
181+ buttonsWidth: decoration.buttonsWidth
182+ }
183+
184+ ApplicationWindow {
185+ id: applicationWindow
186+ objectName: "appWindow"
187+ anchors.left: parent.left
188+ anchors.top: parent.top
189+ anchors.topMargin: root.decorationHeight * Math.min(1, root.showDecoration)
190+ width: implicitWidth
191+ height: implicitHeight
192+ requestedHeight: !counterRotate ? root.requestedHeight - d.requestedDecorationHeight : root.requestedWidth
193+ requestedWidth: !counterRotate ? root.requestedWidth : root.requestedHeight - d.requestedDecorationHeight
194+ property int oldRequestedWidth: requestedWidth
195+ property int oldRequestedHeight: requestedHeight
196+ onRequestedWidthChanged: oldRequestedWidth = requestedWidth
197+ onRequestedHeightChanged: oldRequestedHeight = requestedHeight
198+ focus: true
199+
200+ property real itemScale: 1
201+ property real minSize: Math.min(root.scaleToPreviewSize, Math.min(requestedHeight, Math.min(requestedWidth, Math.min(implicitHeight, implicitWidth))))
202+
203+ transform: [
204+ Rotation {
205+ id: rotationTransform
206+ readonly property int rotationAngle: applicationWindow.application &&
207+ applicationWindow.application.rotatesWindowContents
208+ ? ((360 - applicationWindow.surfaceOrientationAngle) % 360) : 0
209+ origin.x: {
210+ if (rotationAngle == 90) return applicationWindow.height / 2;
211+ else if (rotationAngle == 270) return applicationWindow.width / 2;
212+ else if (rotationAngle == 180) return applicationWindow.width / 2;
213+ else return 0;
214+ }
215+ origin.y: {
216+ if (rotationAngle == 90) return applicationWindow.height / 2;
217+ else if (rotationAngle == 270) return applicationWindow.width / 2;
218+ else if (rotationAngle == 180) return applicationWindow.height / 2;
219+ else return 0;
220+ }
221+ angle: rotationAngle
222+ },
223+ Scale {
224+ xScale: applicationWindow.itemScale
225+ yScale: applicationWindow.itemScale
226+ }
227+ ]
228+
229+ }
230+
231+ Rectangle {
232+ anchors.fill: parent
233+ color: "black"
234+ opacity: root.darkening && !root.showHighlight ? 0.05 : 0
235+ Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
236+ }
237+}
qml/Stages/DesktopSpread.qml → qml/Stage/DesktopSpread.qmlrenamed

The content is the same.

qml/Stages/DesktopSpreadDelegate.qml → qml/Stage/DesktopSpreadDelegate.qmlrenamed

The content is the same.

qml/Stages/FakeMaximizeDelegate.qml → qml/Stage/FakeMaximizeDelegate.qmlrenamed

The content is the same.

qml/Stages/MainViewStyle.qml → qml/Stage/MainViewStyle.qmlrenamed

The content is the same.

qml/Stages/MoveHandler.qml → qml/Stage/MoveHandler.qmlrenamed+2−2
@@ -112,8 +112,8 @@ QtObject {
112112 // Use integer coordinate values to ensure that target is left in a pixel-aligned
113113 // position. Mouse movement could have subpixel precision, yielding a fractional
114114 // mouse position.
115− target.requestedX = Math.round(pos.x - priv.distanceX);
116− target.requestedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
115+ target.windowedX = Math.round(pos.x - priv.distanceX);
116+ target.windowedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
117117
118118 if (sensingPoints) { // edge/corner detection when dragging via the touch overlay
119119 if (sensingPoints.topLeft.x < priv.triggerArea && sensingPoints.topLeft.y < PanelState.panelHeight + priv.triggerArea
qml/Stages/OrientationChangeAnimation.qml → qml/Stage/OrientationChangeAnimation.qmlrenamed

The content is the same.

qml/Stages/PhoneStage.qml → qml/Stage/PhoneStage.qmlrenamed

The content is the same.

qml/Stages/PromptSurfaceAnimations.qml → qml/Stage/PromptSurfaceAnimations.qmlrenamed+2−3
@@ -43,7 +43,7 @@ StateGroup {
4343 SequentialAnimation {
4444 // clip so we don't go out of parent's bounds during spread
4545 PropertyAction { target: root.container.parent; property: "clip"; value: true }
46− UbuntuNumberAnimation { target: root.surfaceItem; property: "y"; to: root.container.height
46+ UbuntuNumberAnimation { target: root.surfaceItem; property: "anchors.topMargin"; to: root.container.height
4747 duration: UbuntuAnimation.BriskDuration }
4848 PropertyAction { target: root.surfaceItem; property: "visible"; value: false }
4949 PropertyAction { target: container.parent; property: "clip"; value: false }
@@ -61,11 +61,10 @@ StateGroup {
6161 // clip so we don't go out of parent's bounds during spread
6262 PropertyAction { target: root.container.parent; property: "clip"; value: true }
6363 ScriptAction { script: {
64− root.surfaceItem.y = root.container.height;
6564 root.surfaceItem.visible = true;
6665 } }
6766 UbuntuNumberAnimation {
68− target: root.surfaceItem; property: "y"; to: 0
67+ target: root.surfaceItem; property: "anchors.topMargin"; from: root.container.height; to: 0
6968 duration: UbuntuAnimation.BriskDuration
7069 }
7170 PropertyAction { target: container.parent; property: "clip"; value: false }
qml/Stages/ResizeGrip.qml → qml/Stage/ResizeGrip.qmlrenamed

The content is the same.

qml/Stages/SideStage.qml → qml/Stage/SideStage.qmlrenamed

The content is the same.

qml/Stages/Splash.qml → qml/Stage/Splash.qmlrenamed

The content is the same.

qml/Stage/Spread/BezierCurve.qmlnew file+45
@@ -0,0 +1,45 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import QtQuick.Window 2.2
20+import 'cubic-bezier.js' as Bezier
21+import 'KeySpline.js' as KeySpline
22+
23+Item {
24+ id: root
25+
26+ property var controlPoint1: {'x':0, 'y':0}
27+ property var controlPoint2: {'x':0, 'y':0}
28+ property var controlPoint3: {'x':0.58, 'y':1}
29+ property var controlPoint4: {'x':1, 'y':1}
30+
31+ function getValues(t) {
32+ if (t<0) t=0
33+ else if (t>1)t=1
34+
35+ return Bezier.getBezier(t, controlPoint1, controlPoint2, controlPoint3, controlPoint4)
36+ }
37+
38+ function getYFromX(x) {
39+ var spline = new KeySpline.keySpline(controlPoint2.x, controlPoint2.y, controlPoint3.x, controlPoint3.y)
40+ if (x<0) x=0
41+ else if (x>1)x=1
42+
43+ return spline.get(x)
44+ }
45+}
qml/Stage/Spread/KeySpline.jsnew file+66
@@ -0,0 +1,66 @@
1+/** MIT License
2+ *
3+ * KeySpline - use bezier curve for transition easing function
4+ * Copyright (c) 2012 Gaetan Renaudeau <renaudeau.gaetan@gmail.com>
5+ *
6+ * Permission is hereby granted, free of charge, to any person obtaining a
7+ * copy of this software and associated documentation files (the "Software"),
8+ * to deal in the Software without restriction, including without limitation
9+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10+ * and/or sell copies of the Software, and to permit persons to whom the
11+ * Software is furnished to do so, subject to the following conditions:
12+ *
13+ * The above copyright notice and this permission notice shall be included in
14+ * all copies or substantial portions of the Software.
15+ *
16+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22+ * DEALINGS IN THE SOFTWARE.
23+ */
24+/**
25+* KeySpline - use bezier curve for transition easing function
26+* is inspired from Firefox's nsSMILKeySpline.cpp
27+* Usage:
28+* var spline = new KeySpline(0.25, 0.1, 0.25, 1.0)
29+* spline.get(x) => returns the easing value | x must be in [0, 1] range
30+*/
31+
32+.pragma library
33+
34+function keySpline (mX1, mY1, mX2, mY2) {
35+
36+ this.get = function(aX) {
37+ if (mX1 == mY1 && mX2 == mY2) return aX; // linear
38+ return CalcBezier(GetTForX(aX), mY1, mY2);
39+ }
40+
41+ function A(aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
42+ function B(aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; }
43+ function C(aA1) { return 3.0 * aA1; }
44+
45+ // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
46+ function CalcBezier(aT, aA1, aA2) {
47+ return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT;
48+ }
49+
50+ // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
51+ function GetSlope(aT, aA1, aA2) {
52+ return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
53+ }
54+
55+ function GetTForX(aX) {
56+ // Newton raphson iteration
57+ var aGuessT = aX;
58+ for (var i = 0; i < 4; ++i) {
59+ var currentSlope = GetSlope(aGuessT, mX1, mX2);
60+ if (currentSlope == 0.0) return aGuessT;
61+ var currentX = CalcBezier(aGuessT, mX1, mX2) - aX;
62+ aGuessT -= currentX / currentSlope;
63+ }
64+ return aGuessT;
65+ }
66+}
qml/Stage/Spread/MathUtils.jsnew file+95
@@ -0,0 +1,95 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+.pragma library
18+
19+/**
20+ * From processing.js: https://raw.githubusercontent.com/processing-js/processing-js/v1.4.8/processing.js
21+ *
22+ * Re-map a number from one range to another. In the example above, the number
23+ * '25' is converted from a value in the range 0..100 into a value that
24+ * ranges from the left edge (0) to the right edge (width) of the screen.
25+ * Numbers outside the range are not clamped to 0 and 1, because out-of-range
26+ * values are often intentional and useful.
27+ *
28+ * @param {Number} value The incoming value to be converted
29+ * @param {Number} istart Lower bound of the value's current range
30+ * @param {Number} istop Upper bound of the value's current range
31+ * @param {Number} ostart Lower bound of the value's target range
32+ * @param {Number} ostop Upper bound of the value's target range
33+ *
34+ * @returns {Number}
35+ */
36+function map(value, istart, istop, ostart, ostop) {
37+ return ostart + (ostop - ostart) * ((value - istart) / (istop - istart))
38+}
39+
40+/**
41+ * Return a value which is always between `min` and `max`
42+ *
43+ * @param {Number} value The current value
44+ * @param {Number} min The minimum value
45+ * @param {Number} max The maximum value
46+ *
47+ * @returns {Number}
48+ */
49+function clamp(value, min, max) {
50+ if (value < min) return min
51+ if (value > max) return max
52+ return value
53+}
54+
55+// calculates the distance from the middle of one rect to middle of other rect
56+function rectDistance(rect1, rect2) {
57+ return pointDistance(Qt.point(rect1.x + rect1.width / 2, rect1.y + rect1.height / 2),
58+ Qt.point(rect2.x + rect2.width / 2, rect2.y + rect2.height / 2))
59+}
60+
61+// calculates the distance between two points
62+function pointDistance(point1, point2) {
63+ return Math.sqrt(Math.pow(point1.x - point2.x, 2) +
64+ Math.pow(point1.y - point2.y, 2)
65+ )
66+}
67+
68+// from http://stackoverflow.com/questions/14616829/java-method-to-find-the-rectangle-that-is-the-intersection-of-two-rectangles-usi
69+function intersectionRect(r1, r2) {
70+ var xmin = Math.max(r1.x, r2.x);
71+ var xmax1 = r1.x + r1.width;
72+ var xmax2 = r2.x + r2.width;
73+ var xmax = Math.min(xmax1, xmax2);
74+ var out = {x:0, y:0, width:0, height:0}
75+ if (xmax > xmin) {
76+ var ymin = Math.max(r1.y, r2.y);
77+ var ymax1 = r1.y + r1.height;
78+ var ymax2 = r2.y + r2.height;
79+ var ymax = Math.min(ymax1, ymax2);
80+ if (ymax > ymin) {
81+ out.x = xmin;
82+ out.y = ymin;
83+ out.width = xmax - xmin;
84+ out.height = ymax - ymin;
85+ }
86+ }
87+ return out;
88+}
89+
90+function easeOutCubic(t) { return (--t)*t*t+1 }
91+
92+function linearAnimation(startProgress, endProgress, startValue, endValue, progress) {
93+ // progress : progressDiff = value : valueDiff => value = progress * valueDiff / progressDiff
94+ return (progress - startProgress) * (endValue - startValue) / (endProgress - startProgress) + startValue;
95+}
qml/Stage/Spread/OpacityMask.qmlnew file+82
@@ -0,0 +1,82 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+import QtQuick 2.4
18+
19+Item {
20+ id: root
21+ anchors.fill: sourceItem
22+ anchors.margins: -units.gu(2)
23+ visible: sourceItem !== null
24+
25+ property var sourceItem: null
26+ property int maskX: 0
27+ property int maskY: 0
28+ property int maskWidth: 0
29+ property int maskHeight: 0
30+
31+ property real opacityValue: 1
32+
33+ Item {
34+ id: opacityMask
35+ anchors.fill: parent
36+
37+ Rectangle {
38+ id: clipRect
39+ color: "black"
40+ x: root.maskX - root.anchors.margins
41+ y: root.maskY - root.anchors.margins
42+ width: root.maskWidth
43+ height: root.maskHeight
44+ opacity: 1 - root.opacityValue
45+ }
46+ }
47+
48+ ShaderEffect {
49+ id: opacityEffect
50+ anchors.fill: parent
51+
52+ property variant source: ShaderEffectSource {
53+ id: shaderEffectSource
54+ sourceItem: root.sourceItem
55+ sourceRect: root.sourceItem ? Qt.rect(sourceItem.x + root.anchors.margins,
56+ sourceItem.y + root.anchors.margins,
57+ sourceItem.width - root.anchors.margins * 2,
58+ sourceItem.height - root.anchors.margins * 2)
59+ : Qt.rect(0,0,0,0)
60+ hideSource: true
61+ }
62+
63+ property var mask: ShaderEffectSource {
64+ sourceItem: opacityMask
65+ hideSource: true
66+ }
67+
68+ fragmentShader: "
69+ varying highp vec2 qt_TexCoord0;
70+ uniform sampler2D source;
71+ uniform sampler2D mask;
72+ void main(void)
73+ {
74+ highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
75+ highp vec4 maskColor = texture2D(mask, qt_TexCoord0);
76+
77+ sourceColor *= 1.0 - maskColor.a;
78+
79+ gl_FragColor = sourceColor;
80+ }"
81+ }
82+}
qml/Stage/Spread/Spread.qmlnew file+162
@@ -0,0 +1,162 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import "MathUtils.js" as MathUtils
20+
21+Item {
22+ id: root
23+
24+ // Information about the environment
25+ property int highlightedIndex: -1
26+ property var model: null
27+ property int leftMargin: 0
28+ property var spreadFlickable
29+
30+ // some config options
31+ property real contentMargin: 0.16 * root.height
32+ property real contentTopMargin: contentMargin
33+ property real contentBottomMargin: 0.35 * contentMargin
34+ property real windowTitleTopMargin: 3/4 * (contentTopMargin - windowTitle.height)
35+ property int stackItemCount: 3
36+ property real leftRotationAngle: 22
37+ property real rightRotationAngle: 32
38+ property real leftStackScale: .82
39+ property real rightStackScale: 1
40+ property real rightEdgeBreakPoint: Math.min(units.gu(40) / root.width, .35)
41+
42+ signal leaveSpread()
43+
44+ // Calculated stuff
45+ readonly property int totalItemCount: model.count
46+ readonly property real leftStackXPos: 0.03 * root.width + leftMargin
47+ readonly property real rightStackXPos: root.width - 1.5 * leftStackXPos + leftMargin
48+
49+ readonly property real stackHeight: spreadItemHeight - appInfoHeight
50+ readonly property real stackWidth: Math.min(leftStackXPos/3, units.gu(1.5))
51+
52+ readonly property real spreadWidth: rightStackXPos - leftStackXPos
53+ readonly property real spreadHeight: root.height
54+ readonly property real spreadItemHeight: spreadHeight - contentTopMargin - contentBottomMargin
55+ readonly property real spreadItemWidth: stackHeight
56+
57+ readonly property real dynamicLeftRotationAngle: leftRotationAngle * rotationAngleFactor
58+ readonly property real dynamicRightRotationAngle: rightRotationAngle * rotationAngleFactor
59+
60+ readonly property real appInfoHeight: {
61+ var screenHeightReferencePoint = 40 // ref screen height in gu
62+ var valueAtReferencePoint = 0.17 // of screen height at the reference point
63+ var appInfoHeightValueChange = -0.0014 // units / gu
64+ var minAppInfoHeight = 0.08
65+ var maxAppInfoHeight = 0.2
66+ var screenHeightInGU = root.height / units.gu(1) // screenHeight in gu
67+
68+ return MathUtils.clamp(valueAtReferencePoint + appInfoHeightValueChange * (screenHeightInGU - screenHeightReferencePoint), minAppInfoHeight, maxAppInfoHeight) * root.height
69+ }
70+
71+ property real rotationAngleFactor: {
72+ var spreadHeightReferencePoint = 28 // reference spread height in gu
73+ var valueAtReferencePoint = 1.3
74+ var rotationAngleValueChange = -0.008 // units / gu
75+ var minRotationAngleFactor = 0.6
76+ var maxRotationAngleFactor = 1.5
77+ var spreadHeightInGU = spreadHeight / units.gu(1)
78+
79+ return MathUtils.clamp(valueAtReferencePoint + rotationAngleValueChange * (spreadHeightInGU - spreadHeightReferencePoint), minRotationAngleFactor, maxRotationAngleFactor)
80+ }
81+ readonly property real itemOverlap: {
82+ var spreadAspectRatioReferencePoint = 1.0 // ref screen height in gu
83+ var valueAtReferencePoint = 0.74 // of screen height at the reference point
84+ var itemOverlapValueChange = -0.068
85+ var minOverlap = 0.55
86+ var maxOverlap = 0.82
87+ var spreadAspectRatio = spreadWidth / stackHeight // spread stack aspect ratio (app info not included)
88+
89+ return MathUtils.clamp(valueAtReferencePoint + itemOverlapValueChange * (spreadAspectRatio - spreadAspectRatioReferencePoint), minOverlap, maxOverlap)
90+ }
91+
92+ readonly property real visibleItemCount: (spreadWidth / spreadItemWidth) / (1 - itemOverlap)
93+
94+ readonly property real spreadTotalWidth: totalItemCount * spreadWidth / visibleItemCount
95+
96+ readonly property real centeringOffset: Math.max(spreadWidth - spreadTotalWidth ,0) / (2 * spreadWidth)
97+
98+
99+ readonly property var curve: BezierCurve {
100+ controlPoint2: {'x': 0.19, 'y': 0.00}
101+ controlPoint3: {'x': 0.91, 'y': 1.00}
102+ }
103+
104+ Label {
105+ id: windowTitle
106+
107+ width: Math.min(implicitWidth, 0.5*root.width)
108+ elide: Qt.ElideMiddle
109+ anchors.horizontalCenter: parent.horizontalCenter
110+ y: windowTitleTopMargin
111+ readonly property var highlightedSurface: root.model ? root.model.surfaceAt(root.highlightedIndex) : null
112+ readonly property var highlightedApp: root.model ? root.model.applicationAt(root.highlightedIndex) : null
113+ text: root.highlightedIndex >= 0 && highlightedSurface && highlightedSurface.name != "" ? highlightedSurface.name :
114+ highlightedApp ? highlightedApp.name : ""
115+ fontSize: root.height < units.gu(85) ? 'medium' : 'large'
116+ color: "white"
117+ opacity: root.highlightedIndex >= 0 ? 1 : 0
118+ Behavior on opacity { UbuntuNumberAnimation { } }
119+ }
120+
121+ Keys.onPressed: {
122+ switch (event.key) {
123+ case Qt.Key_Left:
124+ case Qt.Key_Backtab:
125+ selectPrevious(event.isAutoRepeat)
126+ event.accepted = true;
127+ break;
128+ case Qt.Key_Right:
129+ case Qt.Key_Tab:
130+ selectNext(event.isAutoRepeat)
131+ event.accepted = true;
132+ break;
133+ case Qt.Key_Escape:
134+ highlightedIndex = -1
135+ // Falling through intentionally
136+ case Qt.Key_Enter:
137+ case Qt.Key_Return:
138+ case Qt.Key_Space:
139+ root.leaveSpread();
140+ event.accepted = true;
141+ }
142+ }
143+
144+
145+ function selectNext(isAutoRepeat) {
146+ if (isAutoRepeat && highlightedIndex >= totalItemCount -1) {
147+ return; // AutoRepeat is not allowed to wrap around
148+ }
149+
150+ highlightedIndex = (highlightedIndex + 1) % totalItemCount;
151+ spreadFlickable.snap(highlightedIndex)
152+ }
153+
154+ function selectPrevious(isAutoRepeat) {
155+ if (isAutoRepeat && highlightedIndex == 0) {
156+ return; // AutoRepeat is not allowed to wrap around
157+ }
158+
159+ highlightedIndex = highlightedIndex - 1 >= 0 ? highlightedIndex - 1 : totalItemCount - 1;
160+ spreadFlickable.snap(highlightedIndex)
161+ }
162+}
qml/Stage/Spread/SpreadDelegateInputArea.qmlnew file+183
@@ -0,0 +1,183 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Ubuntu.Gestures 0.1
20+import "../../Components"
21+
22+Item {
23+ id: root
24+
25+ property bool closeable: true
26+ readonly property real minSpeedToClose: units.gu(40)
27+ property bool zeroVelocityCounts: false
28+
29+ readonly property alias distance: d.distance
30+
31+ signal clicked()
32+ signal close()
33+
34+ QtObject {
35+ id: d
36+ property real distance: 0
37+ property bool moving: false
38+ property var dragEvents: []
39+ property real dragVelocity: 0
40+ property int threshold: units.gu(2)
41+
42+ // Can be replaced with a fake implementation during tests
43+ // property var __getCurrentTimeMs: function () { return new Date().getTime() }
44+ property var __dateTime: new function() {
45+ this.getCurrentTimeMs = function() {return new Date().getTime()}
46+ }
47+
48+ function pushDragEvent(event) {
49+ var currentTime = __dateTime.getCurrentTimeMs()
50+ dragEvents.push([currentTime, event.x - event.startX, event.y - event.startY, getEventSpeed(currentTime, event)])
51+ cullOldDragEvents(currentTime)
52+ updateSpeed()
53+ }
54+
55+ function cullOldDragEvents(currentTime) {
56+ // cull events older than 50 ms but always keep the latest 2 events
57+ for (var numberOfCulledEvents = 0; numberOfCulledEvents < dragEvents.length-2; numberOfCulledEvents++) {
58+ // dragEvents[numberOfCulledEvents][0] is the dragTime
59+ if (currentTime - dragEvents[numberOfCulledEvents][0] <= 50) break
60+ }
61+
62+ dragEvents.splice(0, numberOfCulledEvents)
63+ }
64+
65+ function updateSpeed() {
66+ var totalSpeed = 0
67+ for (var i = 0; i < dragEvents.length; i++) {
68+ totalSpeed += dragEvents[i][3]
69+ }
70+
71+ if (zeroVelocityCounts || Math.abs(totalSpeed) > 0.001) {
72+ dragVelocity = totalSpeed / dragEvents.length * 1000
73+ }
74+ }
75+
76+ function getEventSpeed(currentTime, event) {
77+ if (dragEvents.length != 0) {
78+ var lastDrag = dragEvents[dragEvents.length-1]
79+ var duration = Math.max(1, currentTime - lastDrag[0])
80+ return (event.y - event.startY - lastDrag[2]) / duration
81+ } else {
82+ return 0
83+ }
84+ }
85+ }
86+
87+ // Event eater
88+ MouseArea {
89+ anchors.fill: parent
90+ onClicked: root.clicked()
91+ onWheel: wheel.accepted = true
92+ }
93+
94+ MultiPointTouchArea {
95+ anchors.fill: parent
96+ mouseEnabled: false
97+ maximumTouchPoints: 1
98+ property int offset: 0
99+
100+ touchPoints: [
101+ TouchPoint {
102+ id: tp
103+ }
104+ ]
105+
106+ onCanceled: {
107+ d.moving = false
108+ animation.animate("center");
109+ }
110+
111+ onTouchUpdated: {
112+ if (!d.moving) {
113+ if (Math.abs(tp.startY - tp.y) > d.threshold) {
114+ d.moving = true;
115+ d.dragEvents = []
116+ offset = tp.y - tp.startY;
117+ } else {
118+ return;
119+ }
120+ }
121+
122+ d.distance = tp.y - tp.startY - offset
123+ d.pushDragEvent(tp);
124+ }
125+
126+ onReleased: {
127+ if (!d.moving) {
128+ root.clicked()
129+ }
130+
131+ if (!root.closeable) {
132+ animation.animate("center")
133+ return;
134+ }
135+
136+ var touchPoint = touchPoints[0];
137+
138+ if ((d.dragVelocity < -root.minSpeedToClose && d.distance < -units.gu(8)) || d.distance < -root.height / 2) {
139+ animation.animate("up")
140+ } else if ((d.dragVelocity > root.minSpeedToClose && d.distance > units.gu(8)) || d.distance > root.height / 2) {
141+ animation.animate("down")
142+ } else {
143+ animation.animate("center")
144+ }
145+ }
146+ }
147+
148+ UbuntuNumberAnimation {
149+ id: animation
150+ objectName: "closeAnimation"
151+ target: d
152+ property: "distance"
153+ property bool requestClose: false
154+
155+ function animate(direction) {
156+ animation.from = dragArea.distance;
157+ switch (direction) {
158+ case "up":
159+ animation.to = -root.height * 1.5;
160+ requestClose = true;
161+ break;
162+ case "down":
163+ animation.to = root.height * 1.5;
164+ requestClose = true;
165+ break;
166+ default:
167+ animation.to = 0
168+ }
169+ animation.start();
170+ }
171+
172+ onRunningChanged: {
173+ if (!running) {
174+ d.moving = false;
175+ if (requestClose) {
176+ root.close();
177+ } else {
178+ d.distance = 0;
179+ }
180+ }
181+ }
182+ }
183+}
qml/Stage/Spread/SpreadMaths.qmlnew file+78
@@ -0,0 +1,78 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Utils 0.1
20+import "MathUtils.js" as MathUtils
21+
22+Item {
23+ id: root
24+ anchors { left: parent.left; top: parent.top; margins: units.gu(1) }
25+
26+ // Information about the environment
27+ property Item flickable: null
28+ property Spread spread: null
29+ property int itemIndex: 0
30+
31+ // Internal
32+ property real spreadPosition: itemIndex/spread.visibleItemCount - flickable.contentX/spread.spreadWidth // 0 -> left stack, 1 -> right stack
33+ property real leftStackingProgress: MathUtils.clamp(MathUtils.map(spreadPosition, 0, -spread.stackItemCount/spread.visibleItemCount , 0, 1), 0, 1)
34+ property real rightStackingProgress: MathUtils.clamp(MathUtils.map(spreadPosition, 1, 1 + spread.stackItemCount/spread.visibleItemCount , 0, 1), 0, 1)
35+ property real stackingX: (MathUtils.easeOutCubic(rightStackingProgress) - MathUtils.easeOutCubic(leftStackingProgress)) * spread.stackWidth
36+
37+
38+ QtObject {
39+ id: d
40+ property real spreadScale: MathUtils.clamp(
41+ MathUtils.map(spreadPosition, 0, 1, spread.leftStackScale, spread.rightStackScale),
42+ spread.leftStackScale, spread.rightStackScale)
43+
44+ property real selectedScale: (spread.highlightedIndex == itemIndex ? 1.01 : 1)
45+ Behavior on selectedScale { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
46+
47+ }
48+
49+ // Output
50+ readonly property int targetX: spread.leftStackXPos +
51+ spread.spreadWidth * spread.curve.getYFromX(spreadPosition + spread.centeringOffset) +
52+ stackingX
53+
54+ readonly property int targetY: spread.contentTopMargin
55+
56+ readonly property real targetAngle: MathUtils.clamp(
57+ MathUtils.map(targetX, spread.leftStackXPos, spread.rightStackXPos, spread.dynamicLeftRotationAngle, spread.dynamicRightRotationAngle),
58+ Math.min(spread.dynamicLeftRotationAngle, spread.dynamicRightRotationAngle), Math.max(spread.dynamicLeftRotationAngle, spread.dynamicRightRotationAngle))
59+
60+
61+ readonly property real targetScale: d.spreadScale * d.selectedScale
62+
63+ readonly property real shadowOpacity: 0.2 * (1 - rightStackingProgress) * (1 - leftStackingProgress)
64+
65+
66+ readonly property real closeIconOffset: (targetScale - 1) * (-spread.stackHeight / 2)
67+
68+ readonly property real tileInfoOpacity: Math.min(MathUtils.clamp(MathUtils.map(leftStackingProgress, 0 , 1/(spread.stackItemCount*3), 1, 0), 0 , 1),
69+ MathUtils.clamp(MathUtils.map(spreadPosition, 0.9 , 1, 1, 0), 0 , 1)) /** MathUtils.map(curvedSwitcherProgress, 0.7, 0.9, 0, 1)*/
70+
71+ readonly property bool itemVisible: {
72+ var leftStackHidden = spreadPosition < -(spread.stackItemCount + 1)/spread.visibleItemCount
73+ // don't hide the rightmost
74+ var rightStackHidden = (spreadPosition > 1 + (spread.stackItemCount)/spread.visibleItemCount) && itemIndex !== spread.totalItemCount - 1
75+ return !leftStackHidden && !rightStackHidden
76+ }
77+
78+}
qml/Stage/Spread/StagedRightEdgeMaths.qmlnew file+174
@@ -0,0 +1,174 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Unity.Application 0.1
20+import "MathUtils.js" as MathUtils
21+
22+QtObject {
23+ id: root
24+
25+ // Input
26+ property int itemIndex: 0
27+ property real progress: 0
28+ property int sceneWidth: 0
29+ property int sideStageWidth: 0
30+ property int sceneHeight: 0
31+ property int targetX: 0
32+ property int startY: 0
33+ property int targetY: 0
34+ property real startAngle: 30
35+ property real targetAngle: 0
36+ property int targetHeight: 0
37+ property real startScale: 1.3
38+ property real targetScale: 0
39+ property real breakPoint: units.gu(15) / sceneWidth
40+
41+ property bool isMainStageApp: false
42+ property bool isSideStageApp: false
43+ property bool sideStageOpen: false
44+ property int nextInStack: 0
45+ property int shuffledZ: 0
46+
47+
48+ // Config
49+ property int tileDistance: units.gu(10)
50+
51+ // Output
52+
53+ readonly property real scaleToPreviewProgress: {
54+ return progress < breakPoint ? 0 : MathUtils.clamp(MathUtils.linearAnimation(breakPoint, 1, 0, 1, progress), 0, 1)
55+ }
56+ readonly property int animatedWidth: {
57+ return progress < breakPoint ? root.sceneHeight : MathUtils.linearAnimation(breakPoint, 1, root.sceneWidth, targetHeight, progress)
58+ }
59+
60+ readonly property int animatedHeight: {
61+ return progress < breakPoint ? root.sceneHeight : MathUtils.linearAnimation(breakPoint, 1, root.sceneHeight, targetHeight, progress)
62+ }
63+
64+
65+ readonly property int animatedX: {
66+ var nextStage = appRepeater.itemAt(nextInStack) ? appRepeater.itemAt(nextInStack).stage : ApplicationInfoInterface.MainStage;
67+
68+ var startX = 0;
69+ if (isMainStageApp) {
70+ if (progress < breakPoint) {
71+ if (nextStage == ApplicationInfoInterface.MainStage) {
72+ return MathUtils.linearAnimation(0, breakPoint, 0, -units.gu(4), progress);
73+ } else {
74+ return 0;
75+ }
76+ } else {
77+ if (nextStage == ApplicationInfoInterface.MainStage) {
78+ return MathUtils.linearAnimation(breakPoint, 1, -units.gu(4), targetX, progress);
79+ } else {
80+ return MathUtils.linearAnimation(breakPoint, 1, 0, targetX, progress);
81+ }
82+ }
83+ } else if (isSideStageApp) {
84+ startX = sceneWidth - sideStageWidth;
85+ } else if (itemIndex == nextInStack && itemIndex <= 2 && priv.sideStageDelegate && nextStage == ApplicationInfoInterface.MainStage) {
86+ startX = sceneWidth - sideStageWidth;
87+ } else {
88+ var stageCount = (priv.mainStageDelegate ? 1 : 0) + (priv.sideStageDelegate ? 1 : 0)
89+ startX = sceneWidth + Math.max(0, itemIndex - stageCount - 1) * tileDistance;
90+ }
91+
92+ if (itemIndex == nextInStack) {
93+ if (progress < breakPoint) {
94+ return MathUtils.linearAnimation(0, breakPoint, startX, startX * (1 - breakPoint), progress)
95+ }
96+ return MathUtils.linearAnimation(breakPoint, 1, startX * (1 - breakPoint), targetX, progress)
97+ }
98+
99+ if (progress < breakPoint) {
100+ return startX;
101+ }
102+
103+ return MathUtils.linearAnimation(breakPoint, 1, startX, targetX, progress)
104+
105+ }
106+
107+ readonly property int animatedY: progress < breakPoint ? startY : MathUtils.linearAnimation(breakPoint, 1, startY, targetY, progress)
108+
109+ readonly property int animatedZ: {
110+ if (progress < breakPoint + (1 - breakPoint) / 2) {
111+ return shuffledZ
112+ }
113+ return itemIndex;
114+ }
115+
116+ readonly property real animatedAngle: {
117+ var nextStage = appRepeater.itemAt(nextInStack) ? appRepeater.itemAt(nextInStack).stage : ApplicationInfoInterface.MainStage;
118+
119+ var startAngle = 0;
120+ if (isMainStageApp) {
121+ startAngle = 0;
122+ } else if (isSideStageApp) {
123+ startAngle = 0;
124+ } else {
125+ if (stage == ApplicationInfoInterface.SideStage && itemIndex == nextInStack && !sideStageOpen) {
126+ startAngle = 0;
127+ } else {
128+ startAngle = root.startAngle;
129+ }
130+ }
131+
132+ if ((itemIndex == nextInStack)
133+ || (isMainStageApp && nextStage === ApplicationInfoInterface.MainStage)
134+ || (isSideStageApp && nextStage === ApplicationInfoInterface.SideStage)) {
135+ return MathUtils.linearAnimation(0, 1, startAngle, targetAngle, progress);
136+ }
137+
138+ if (progress < breakPoint) {
139+ return 0;
140+ }
141+ return MathUtils.linearAnimation(breakPoint, 1, startAngle, targetAngle, progress);
142+ }
143+
144+ readonly property real animatedScale: {
145+ var pullingInSideStage = itemIndex == nextInStack && stage == ApplicationInfoInterface.SideStage && !sideStageOpen;
146+
147+ var startScale = 1;
148+ if (isMainStageApp) {
149+ startScale = 1;
150+ } else if (isSideStageApp) {
151+ startScale = 1;
152+ } else {
153+ if (pullingInSideStage) {
154+ startScale = 1
155+ } else {
156+ startScale = root.startScale;
157+ }
158+ }
159+
160+ if (progress < breakPoint) {
161+ if (itemIndex == nextInStack && (sideStageOpen || stage == ApplicationInfoInterface.MainStage)) {
162+ return MathUtils.linearAnimation(0, 1, startScale, targetScale, progress);
163+ }
164+ return startScale;
165+ }
166+ if (itemIndex == nextInStack) {
167+ return MathUtils.linearAnimation(0, 1, startScale, targetScale, progress)
168+ }
169+
170+ return MathUtils.linearAnimation(breakPoint, 1, startScale, targetScale, progress)
171+ }
172+
173+ readonly property bool itemVisible: true //animatedX < sceneWidth
174+}
qml/Stage/Spread/WindowedRightEdgeMaths.qmlnew file+81
@@ -0,0 +1,81 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Unity.Application 0.1
20+import "MathUtils.js" as MathUtils
21+
22+QtObject {
23+ id: root
24+
25+ // Input
26+ property int itemIndex: 0
27+ property int normalZ: 0
28+ property real progress: 0
29+ property int startWidth: 0
30+ property int startHeight: 0
31+ property int startX: 0
32+ property int targetX: 0
33+ property int startY: 0
34+ property int targetY: 0
35+// property real startAngle: 40
36+ property real targetAngle: 0
37+ property int targetHeight: 0
38+ property real targetScale: 0
39+
40+ // Config
41+ property real breakPoint: 0.4
42+
43+ // Output
44+
45+ readonly property real scaleToPreviewProgress: {
46+ return progress < breakPoint ? 0 : MathUtils.clamp(MathUtils.linearAnimation(breakPoint, 1, 0, 1, progress), 0, 1)
47+ }
48+ readonly property int animatedWidth: {
49+ return progress < breakPoint ? root.startWidth : MathUtils.linearAnimation(breakPoint, 1, root.startWidth, targetHeight, progress)
50+ }
51+
52+ readonly property int animatedHeight: {
53+ return progress < breakPoint ? root.startHeight : MathUtils.linearAnimation(breakPoint, 1, root.startHeight, targetHeight, progress)
54+ }
55+
56+ readonly property int animatedX: {
57+ if (progress < breakPoint) {
58+ return startX;
59+ }
60+ return MathUtils.linearAnimation(breakPoint, 1, startX, targetX, progress)
61+ }
62+
63+ readonly property int animatedY: progress < breakPoint ? startY : MathUtils.linearAnimation(breakPoint, 1, startY, targetY, progress)
64+
65+ readonly property real animatedAngle: progress < breakPoint ? 0 : MathUtils.linearAnimation(breakPoint, 1, 0, targetAngle, progress);
66+
67+ readonly property real decorationHeight: progress < breakPoint ? 1 : MathUtils.linearAnimation(breakPoint, 1, 1, 0, progress);
68+
69+ readonly property int animatedZ: {
70+ if (progress < breakPoint + (1 - breakPoint) / 2) {
71+ return itemIndex == 1 ? normalZ + 2 : normalZ
72+ }
73+ return itemIndex
74+ }
75+
76+ readonly property real opacityMask: itemIndex == 1 ? MathUtils.linearAnimation(0, breakPoint, 0, 1, progress) : 1
77+
78+ readonly property real animatedScale: progress < breakPoint ? 1 : MathUtils.linearAnimation(breakPoint, 1, 1, targetScale, progress)
79+
80+// readonly property bool itemVisible: true //animatedX < sceneWidth
81+}
qml/Stage/Spread/cubic-bezier.jsnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+//====================================\\
18+// 13thParallel.org Beziér Curve Code \\
19+// by Dan Pupius (www.pupius.net) \\
20+//====================================\\
21+
22+
23+var coord = function (x,y) {
24+ if(!x) var x=0;
25+ if(!y) var y=0;
26+ return {x: x, y: y};
27+}
28+
29+function B4(t) { return t*t*t }
30+function B3(t) { return 3*t*t*(1-t) }
31+function B2(t) { return 3*t*(1-t)*(1-t) }
32+function B1(t) { return (1-t)*(1-t)*(1-t) }
33+
34+var getBezier = function(percent,C1,C2,C3,C4) {
35+ var pos = new coord();
36+ pos.x = C1.x*B1(percent) + C2.x*B2(percent) + C3.x*B3(percent) + C4.x*B4(percent);
37+ pos.y = C1.y*B1(percent) + C2.y*B2(percent) + C3.y*B3(percent) + C4.y*B4(percent);
38+ return pos;
39+}
qml/Stages/SpreadDelegate.qml → qml/Stage/SpreadDelegate.qmlrenamed

The content is the same.

qml/Stages/SpreadMaths.qml → qml/Stage/SpreadMaths.qmlrenamed

The content is the same.

qml/Stage/Stage.qmlnew file+1881
@@ -0,0 +1,1881 @@
1+/*
2+ * Copyright (C) 2014-2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Unity.Application 0.1
20+import "../Components/PanelState"
21+import "../Components"
22+import Utils 0.1
23+import Ubuntu.Gestures 0.1
24+import GlobalShortcut 1.0
25+import GSettings 1.0
26+import "Spread"
27+import "Spread/MathUtils.js" as MathUtils
28+
29+FocusScope {
30+ id: root
31+ anchors.fill: parent
32+
33+ property QtObject applicationManager
34+ property QtObject topLevelSurfaceList
35+ property bool altTabPressed
36+ property url background
37+ property int dragAreaWidth
38+ property bool interactive
39+ property bool keepDashRunning: true
40+ property real nativeHeight
41+ property real nativeWidth
42+ property QtObject orientations
43+ property int shellOrientation
44+ property int shellOrientationAngle
45+ property bool spreadEnabled: true // If false, animations and right edge will be disabled
46+ property bool suspended
47+ property int leftMargin: 0
48+ property bool oskEnabled: false
49+ property rect inputMethodRect
50+
51+ // Configuration
52+ property string mode: "staged"
53+ property real leftEdgeDragProgress: 0
54+
55+ // Used by the tutorial code
56+ readonly property bool spreadShown: state == "spread"
57+ readonly property real rightEdgeDragProgress: rightEdgeDragArea.progress // How far left the stage has been dragged
58+
59+ // used by the snap windows (edge maximize) feature
60+ readonly property alias previewRectangle: fakeRectangle
61+
62+ readonly property var mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null
63+
64+ // application windows never rotate independently
65+ property int mainAppWindowOrientationAngle: shellOrientationAngle
66+
67+ property bool orientationChangesEnabled: priv.focusedAppDelegate && priv.focusedAppDelegate.orientationChangesEnabled
68+
69+ property int supportedOrientations: {
70+ if (mainApp) {
71+ switch (mode) {
72+ case "staged":
73+ return mainApp.supportedOrientations;
74+ case "stagedWithSideStage":
75+ var orientations = mainApp.supportedOrientations;
76+ orientations |= Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation;
77+ if (priv.sideStageItemId) {
78+ // If we have a sidestage app, support Portrait orientation
79+ // so that it will switch the sidestage app to mainstage on rotate to portrait
80+ orientations |= Qt.PortraitOrientation|Qt.InvertedPortraitOrientation;
81+ }
82+ return orientations;
83+ }
84+ }
85+
86+ return Qt.PortraitOrientation |
87+ Qt.LandscapeOrientation |
88+ Qt.InvertedPortraitOrientation |
89+ Qt.InvertedLandscapeOrientation;
90+ }
91+
92+
93+ onAltTabPressedChanged: priv.goneToSpread = altTabPressed
94+
95+ property Item itemConfiningMouseCursor: !spreadShown && priv.focusedAppDelegate && priv.focusedAppDelegate.surface &&
96+ priv.focusedAppDelegate.surface.confinesMousePointer ?
97+ priv.focusedAppDelegate.clientAreaItem : null;
98+
99+ signal itemSnapshotRequested(Item item)
100+
101+ // functions to be called from outside
102+ function updateFocusedAppOrientation() { /* TODO */ }
103+ function updateFocusedAppOrientationAnimated() { /* TODO */}
104+ function pushRightEdge(amount) {
105+ edgeBarrier.push(amount);
106+ }
107+
108+ onSpreadEnabledChanged: {
109+ if (!spreadEnabled && root.state == "spread") {
110+ priv.goneToSpread = false;
111+ }
112+ }
113+
114+ GSettings {
115+ id: lifecycleExceptions
116+ schema.id: "com.canonical.qtmir"
117+ }
118+
119+ function isExemptFromLifecycle(appId) {
120+ var shortAppId = appId.split('_')[0];
121+ for (var i = 0; i < lifecycleExceptions.lifecycleExemptAppids.length; i++) {
122+ if (shortAppId === lifecycleExceptions.lifecycleExemptAppids[i]) {
123+ return true;
124+ }
125+ }
126+ return false;
127+ }
128+
129+ GlobalShortcut {
130+ id: closeFocusedShortcut
131+ shortcut: Qt.AltModifier|Qt.Key_F4
132+ onTriggered: {
133+ if (priv.focusedAppDelegate && !priv.focusedAppDelegate.isDash) {
134+ priv.focusedAppDelegate.close();
135+ }
136+ }
137+ }
138+
139+ GlobalShortcut {
140+ id: showSpreadShortcut
141+ shortcut: Qt.MetaModifier|Qt.Key_W
142+ onTriggered: priv.goneToSpread = true
143+ }
144+
145+ GlobalShortcut {
146+ id: minimizeAllShortcut
147+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_D
148+ onTriggered: priv.minimizeAllWindows()
149+ active: root.state == "windowed"
150+ }
151+
152+ GlobalShortcut {
153+ id: maximizeWindowShortcut
154+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
155+ onTriggered: priv.focusedAppDelegate.maximize()
156+ active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximized
157+ }
158+
159+ GlobalShortcut {
160+ id: maximizeWindowLeftShortcut
161+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
162+ onTriggered: priv.focusedAppDelegate.maximizeLeft()
163+ active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
164+ }
165+
166+ GlobalShortcut {
167+ id: maximizeWindowRightShortcut
168+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
169+ onTriggered: priv.focusedAppDelegate.maximizeRight()
170+ active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
171+ }
172+
173+ GlobalShortcut {
174+ id: minimizeRestoreShortcut
175+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
176+ onTriggered: priv.focusedAppDelegate.anyMaximized
177+ ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
178+ active: root.state == "windowed" && priv.focusedAppDelegate
179+ }
180+
181+ GlobalShortcut {
182+ shortcut: Qt.AltModifier|Qt.Key_Print
183+ onTriggered: root.itemSnapshotRequested(priv.focusedAppDelegate)
184+ active: priv.focusedAppDelegate !== null
185+ }
186+
187+ QtObject {
188+ id: priv
189+ objectName: "DesktopStagePrivate"
190+
191+ property var focusedAppDelegate: null
192+ property var foregroundMaximizedAppDelegate: null // for stuff like drop shadow and focusing maximized app by clicking panel
193+
194+ property bool goneToSpread: false
195+ property int closingIndex: -1
196+ property int animationDuration: UbuntuAnimation.FastDuration
197+
198+ function updateForegroundMaximizedApp() {
199+ var found = false;
200+ for (var i = 0; i < appRepeater.count && !found; i++) {
201+ var item = appRepeater.itemAt(i);
202+ if (item && item.visuallyMaximized) {
203+ foregroundMaximizedAppDelegate = item;
204+ found = true;
205+ }
206+ }
207+ if (!found) {
208+ foregroundMaximizedAppDelegate = null;
209+ }
210+ }
211+
212+ function minimizeAllWindows() {
213+ for (var i = 0; i < appRepeater.count; i++) {
214+ var appDelegate = appRepeater.itemAt(i);
215+ if (appDelegate && !appDelegate.minimized) {
216+ appDelegate.minimize();
217+ }
218+ }
219+ }
220+
221+ function focusNext() {
222+ for (var i = 0; i < appRepeater.count; i++) {
223+ var appDelegate = appRepeater.itemAt(i);
224+ if (appDelegate && !appDelegate.minimized) {
225+ appDelegate.focus = true;
226+ return;
227+ }
228+ }
229+ }
230+
231+ readonly property bool sideStageEnabled: root.mode === "stagedWithSideStage" &&
232+ (root.shellOrientation == Qt.LandscapeOrientation ||
233+ root.shellOrientation == Qt.InvertedLandscapeOrientation)
234+
235+ property var mainStageDelegate: null
236+ property var sideStageDelegate: null
237+ property int mainStageItemId: 0
238+ property int sideStageItemId: 0
239+ property string mainStageAppId: ""
240+ property string sideStageAppId: ""
241+
242+ onSideStageDelegateChanged: {
243+ if (!sideStageDelegate) {
244+ sideStage.hide();
245+ }
246+ }
247+
248+ function updateMainAndSideStageIndexes() {
249+ if (root.mode != "stagedWithSideStage") {
250+ priv.sideStageDelegate = null;
251+ priv.sideStageItemId = 0;
252+ priv.sideStageAppId = "";
253+ priv.mainStageDelegate = appRepeater.itemAt(0);
254+ priv.mainStageAppId = topLevelSurfaceList.idAt(0);
255+ priv.mainStageAppId = topLevelSurfaceList.applicationAt(0) ? topLevelSurfaceList.applicationAt(0).appId : ""
256+ return;
257+ }
258+
259+ var choseMainStage = false;
260+ var choseSideStage = false;
261+
262+ if (!root.topLevelSurfaceList)
263+ return;
264+
265+ for (var i = 0; i < appRepeater.count && (!choseMainStage || !choseSideStage); ++i) {
266+ var appDelegate = appRepeater.itemAt(i);
267+ if (!appDelegate) {
268+ // This might happen during startup phase... If the delegate appears and claims focus
269+ // things are updated and appRepeater.itemAt(x) still returns null while appRepeater.count >= x
270+ // Lets just skip it, on startup it will be generated at a later point too...
271+ continue;
272+ }
273+ if (sideStage.shown && appDelegate.stage == ApplicationInfoInterface.SideStage
274+ && !choseSideStage) {
275+ priv.sideStageDelegate = appDelegate
276+ priv.sideStageItemId = root.topLevelSurfaceList.idAt(i);
277+ priv.sideStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
278+ choseSideStage = true;
279+ } else if (!choseMainStage && appDelegate.stage == ApplicationInfoInterface.MainStage) {
280+ priv.mainStageDelegate = appDelegate;
281+ priv.mainStageItemId = root.topLevelSurfaceList.idAt(i);
282+ priv.mainStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
283+ choseMainStage = true;
284+ }
285+ }
286+ if (!choseMainStage && priv.mainStageDelegate) {
287+ priv.mainStageDelegate = null;
288+ priv.mainStageItemId = 0;
289+ priv.mainStageAppId = "";
290+ }
291+ if (!choseSideStage && priv.sideStageDelegate) {
292+ priv.sideStageDelegate = null;
293+ priv.sideStageItemId = 0;
294+ priv.sideStageAppId = "";
295+ }
296+ }
297+
298+ property int nextInStack: {
299+ var mainStageIndex = priv.mainStageDelegate ? priv.mainStageDelegate.itemIndex : -1;
300+ var sideStageIndex = priv.sideStageDelegate ? priv.sideStageDelegate.itemIndex : -1;
301+ if (sideStageIndex == -1) {
302+ return topLevelSurfaceList.count > 1 ? 1 : -1;
303+ }
304+ if (mainStageIndex == 0 || sideStageIndex == 0) {
305+ if (mainStageIndex == 1 || sideStageIndex == 1) {
306+ return topLevelSurfaceList.count > 2 ? 2 : -1;
307+ }
308+ return 1;
309+ }
310+ return -1;
311+ }
312+
313+ readonly property real virtualKeyboardHeight: root.inputMethodRect.height
314+ }
315+
316+ Component.onCompleted: priv.updateMainAndSideStageIndexes();
317+
318+ Connections {
319+ target: PanelState
320+ onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
321+ onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.minimize(); } }
322+ onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.restoreFromMaximized(); } }
323+ }
324+
325+ Binding {
326+ target: PanelState
327+ property: "buttonsVisible"
328+ value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized // FIXME for Locally integrated menus
329+ }
330+
331+ Binding {
332+ target: PanelState
333+ property: "title"
334+ value: {
335+ if (priv.focusedAppDelegate !== null) {
336+ if (priv.focusedAppDelegate.maximized)
337+ return priv.focusedAppDelegate.title
338+ else
339+ return priv.focusedAppDelegate.appName
340+ }
341+ return ""
342+ }
343+ when: priv.focusedAppDelegate
344+ }
345+
346+ Binding {
347+ target: PanelState
348+ property: "dropShadow"
349+ value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppDelegate !== null && mode == "windowed"
350+ }
351+
352+ Binding {
353+ target: PanelState
354+ property: "closeButtonShown"
355+ value: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized && !priv.focusedAppDelegate.isDash
356+ }
357+
358+ Component.onDestruction: {
359+ PanelState.title = "";
360+ PanelState.buttonsVisible = false;
361+ PanelState.dropShadow = false;
362+ }
363+
364+ Instantiator {
365+ model: root.applicationManager
366+ delegate: QtObject {
367+ property var stateBinding: Binding {
368+ readonly property bool isDash: model.application ? model.application.appId == "unity8-dash" : false
369+ target: model.application
370+ property: "requestedState"
371+
372+ // TODO: figure out some lifecycle policy, like suspending minimized apps
373+ // or something if running windowed.
374+ // TODO: If the device has a dozen suspended apps because it was running
375+ // in staged mode, when it switches to Windowed mode it will suddenly
376+ // resume all those apps at once. We might want to avoid that.
377+ value: root.mode === "windowed"
378+ || (isDash && root.keepDashRunning)
379+ || (!root.suspended && model.application && priv.focusedAppDelegate &&
380+ (priv.focusedAppDelegate.appId === model.application.appId ||
381+ priv.mainStageAppId === model.application.appId ||
382+ priv.sideStageAppId === model.application.appId))
383+ ? ApplicationInfoInterface.RequestedRunning
384+ : ApplicationInfoInterface.RequestedSuspended
385+ }
386+
387+ property var lifecycleBinding: Binding {
388+ target: model.application
389+ property: "exemptFromLifecycle"
390+ value: model.application
391+ ? (!model.application.isTouchApp || isExemptFromLifecycle(model.application.appId))
392+ : false
393+ }
394+ }
395+ }
396+
397+ Binding {
398+ target: MirFocusController
399+ property: "focusedSurface"
400+ value: priv.focusedAppDelegate ? priv.focusedAppDelegate.focusedSurface : null
401+ when: !appRepeater.startingUp && root.parent
402+ }
403+
404+ states: [
405+ State {
406+ name: "spread"; when: priv.goneToSpread
407+ PropertyChanges { target: floatingFlickable; enabled: true }
408+ PropertyChanges { target: spreadItem; focus: true }
409+ PropertyChanges { target: hoverMouseArea; enabled: true }
410+ PropertyChanges { target: rightEdgeDragArea; enabled: false }
411+ PropertyChanges { target: cancelSpreadMouseArea; enabled: true }
412+ },
413+ State {
414+ name: "stagedRightEdge"; when: (rightEdgeDragArea.dragging || edgeBarrier.progress > 0) && root.mode == "staged"
415+ },
416+ State {
417+ name: "sideStagedRightEdge"; when: (rightEdgeDragArea.dragging || edgeBarrier.progress > 0) && root.mode == "stagedWithSideStage"
418+ PropertyChanges {
419+ target: sideStage
420+ opacity: priv.sideStageDelegate.x === sideStage.x ? 1 : 0
421+ visible: true
422+ }
423+ },
424+ State {
425+ name: "windowedRightEdge"; when: (rightEdgeDragArea.dragging || edgeBarrier.progress > 0) && root.mode == "windowed"
426+ },
427+ State {
428+ name: "staged"; when: root.mode === "staged"
429+ },
430+ State {
431+ name: "stagedWithSideStage"; when: root.mode === "stagedWithSideStage"
432+ PropertyChanges { target: triGestureArea; enabled: true }
433+ PropertyChanges { target: sideStage; visible: true }
434+ },
435+ State {
436+ name: "windowed"; when: root.mode === "windowed"
437+ }
438+ ]
439+ transitions: [
440+ Transition {
441+ from: "stagedRightEdge,sideStagedRightEdge,windowedRightEdge"; to: "spread"
442+ PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
443+ },
444+ Transition {
445+ to: "spread"
446+ PropertyAction { target: spreadItem; property: "highlightedIndex"; value: appRepeater.count > 1 ? 1 : 0 }
447+ PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
448+ },
449+ Transition {
450+ from: "spread"
451+ SequentialAnimation {
452+ ScriptAction {
453+ script: {
454+ var item = appRepeater.itemAt(Math.max(0, spreadItem.highlightedIndex));
455+ if (item.stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
456+ sideStage.show();
457+ }
458+ item.playFocusAnimation();
459+ }
460+ }
461+ PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
462+ }
463+ },
464+ Transition {
465+ to: "stagedRightEdge"
466+ PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
467+ },
468+ Transition {
469+ to: "stagedWithSideStage"
470+ ScriptAction { script: priv.updateMainAndSideStageIndexes(); }
471+ }
472+
473+ ]
474+
475+ MouseArea {
476+ id: cancelSpreadMouseArea
477+ anchors.fill: parent
478+ enabled: false
479+ onClicked: priv.goneToSpread = false
480+ }
481+
482+ FocusScope {
483+ id: appContainer
484+ objectName: "appContainer"
485+ anchors.fill: parent
486+ focus: true
487+
488+ Wallpaper {
489+ id: wallpaper
490+ anchors.fill: parent
491+ source: root.background
492+ // Make sure it's the lowest item. Due to the left edge drag we sometimes need
493+ // to put the dash at -1 and we don't want it behind the Wallpaper
494+ z: -2
495+ }
496+
497+ Spread {
498+ id: spreadItem
499+ objectName: "spreadItem"
500+ anchors.fill: appContainer
501+ leftMargin: root.leftMargin
502+ model: root.topLevelSurfaceList
503+ spreadFlickable: floatingFlickable
504+ z: 10
505+
506+ onLeaveSpread: {
507+ priv.goneToSpread = false;
508+ }
509+ }
510+
511+ Connections {
512+ target: root.topLevelSurfaceList
513+ onListChanged: priv.updateMainAndSideStageIndexes()
514+ }
515+
516+
517+ DropArea {
518+ objectName: "MainStageDropArea"
519+ anchors {
520+ left: parent.left
521+ top: parent.top
522+ bottom: parent.bottom
523+ }
524+ width: appContainer.width - sideStage.width
525+ enabled: sideStage.enabled
526+
527+ onDropped: {
528+ drop.source.appDelegate.saveStage(ApplicationInfoInterface.MainStage);
529+ drop.source.appDelegate.focus = true;
530+ }
531+ keys: "SideStage"
532+ }
533+
534+ SideStage {
535+ id: sideStage
536+ objectName: "sideStage"
537+ shown: false
538+ height: appContainer.height
539+ x: appContainer.width - width
540+ visible: false
541+ Behavior on opacity { UbuntuNumberAnimation {} }
542+ z: {
543+ if (!priv.mainStageItemId) return 0;
544+
545+ if (priv.sideStageItemId && priv.nextInStack > 0) {
546+
547+ // Due the order in which bindings are evaluated, this might be triggered while shuffling
548+ // the list and index doesn't yet match with itemIndex (even though itemIndex: index)
549+ // Let's walk the list and compare itemIndex to make sure we have the correct one.
550+ var nextDelegateInStack = -1;
551+ for (var i = 0; i < appRepeater.count; i++) {
552+ if (appRepeater.itemAt(i).itemIndex == priv.nextInStack) {
553+ nextDelegateInStack = appRepeater.itemAt(i);
554+ break;
555+ }
556+ }
557+
558+ if (nextDelegateInStack.stage === ApplicationInfoInterface.MainStage) {
559+ // if the next app in stack is a main stage app, put the sidestage on top of it.
560+ return 2;
561+ }
562+ return 1;
563+ }
564+
565+ return 1;
566+ }
567+
568+ onShownChanged: {
569+ if (!shown && priv.mainStageDelegate) {
570+ priv.mainStageDelegate.claimFocus();
571+ }
572+ }
573+
574+ DropArea {
575+ id: sideStageDropArea
576+ objectName: "SideStageDropArea"
577+ anchors.fill: parent
578+
579+ property bool dropAllowed: true
580+
581+ onEntered: {
582+ dropAllowed = drag.keys != "Disabled";
583+ }
584+ onExited: {
585+ dropAllowed = true;
586+ }
587+ onDropped: {
588+ if (drop.keys == "MainStage") {
589+ drop.source.appDelegate.saveStage(ApplicationInfoInterface.SideStage);
590+ drop.source.appDelegate.focus = true;
591+ }
592+ }
593+ drag {
594+ onSourceChanged: {
595+ if (!sideStageDropArea.drag.source) {
596+ dropAllowed = true;
597+ }
598+ }
599+ }
600+ }
601+ }
602+
603+ TopLevelSurfaceRepeater {
604+ id: appRepeater
605+ model: topLevelSurfaceList
606+ objectName: "appRepeater"
607+
608+ delegate: FocusScope {
609+ id: appDelegate
610+ objectName: "appDelegate_" + model.id
611+ property int itemIndex: index // We need this from outside the repeater
612+ // z might be overriden in some cases by effects, but we need z ordering
613+ // to calculate occlusion detection
614+ property int normalZ: topLevelSurfaceList.count - index
615+ onNormalZChanged: {
616+ if (visuallyMaximized) {
617+ priv.updateForegroundMaximizedApp();
618+ }
619+ }
620+ z: normalZ
621+
622+ // Normally we want x/y where we request it to be. Width/height of our delegate will
623+ // match what the actual surface size is.
624+ // Don't write to those, they will be set by states
625+ x: requestedX
626+ y: requestedY
627+ width: decoratedWindow.implicitWidth
628+ height: decoratedWindow.implicitHeight
629+
630+ // requestedX/Y/width/height is what we ask the actual surface to be.
631+ // Do not write to those, they will be set by states
632+ property real requestedX: windowedX
633+ property real requestedY: windowedY
634+ property real requestedWidth: windowedWidth
635+ property real requestedHeight: windowedHeight
636+
637+ // In those are for windowed mode. Those values basically store the window's properties
638+ // when having a floating window. If you want to move/resize a window in normal mode, this is what you want to write to.
639+ property real windowedX
640+ property real windowedY
641+ property real windowedWidth
642+ property real windowedHeight
643+
644+ // unlike windowedX/Y, this is the last known grab position before being pushed against edges/corners
645+ // when restoring, the window should return to these, not to the place where it was dropped near the edge
646+ property real restoredX
647+ property real restoredY
648+
649+ Binding {
650+ target: appDelegate
651+ property: "y"
652+ value: appDelegate.requestedY -
653+ Math.min(appDelegate.requestedY - PanelState.panelHeight,
654+ Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
655+ when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
656+ && root.inputMethodRect.height > 0
657+
658+ }
659+
660+ Behavior on x { id: xBehavior; enabled: priv.closingIndex >= 0; UbuntuNumberAnimation { onRunningChanged: if (!running) priv.closingIndex = -1} }
661+
662+ Connections {
663+ target: root
664+ onShellOrientationAngleChanged: {
665+ // at this point decoratedWindow.surfaceOrientationAngle is the old shellOrientationAngle
666+ if (application && application.rotatesWindowContents) {
667+ if (root.state == "windowed") {
668+ var angleDiff = decoratedWindow.surfaceOrientationAngle - shellOrientationAngle;
669+ angleDiff = (360 + angleDiff) % 360;
670+ if (angleDiff === 90 || angleDiff === 270) {
671+ var aux = decoratedWindow.requestedHeight;
672+ decoratedWindow.requestedHeight = decoratedWindow.requestedWidth + decoratedWindow.decorationHeight;
673+ decoratedWindow.requestedWidth = aux - decoratedWindow.decorationHeight;
674+ }
675+ }
676+ decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
677+ } else {
678+ decoratedWindow.surfaceOrientationAngle = 0;
679+ }
680+ }
681+ }
682+ Connections {
683+ target: priv
684+ onSideStageEnabledChanged: refreshStage()
685+ }
686+
687+ readonly property alias application: decoratedWindow.application
688+ readonly property alias minimumWidth: decoratedWindow.minimumWidth
689+ readonly property alias minimumHeight: decoratedWindow.minimumHeight
690+ readonly property alias maximumWidth: decoratedWindow.maximumWidth
691+ readonly property alias maximumHeight: decoratedWindow.maximumHeight
692+ readonly property alias widthIncrement: decoratedWindow.widthIncrement
693+ readonly property alias heightIncrement: decoratedWindow.heightIncrement
694+
695+ readonly property bool maximized: windowState === WindowStateStorage.WindowStateMaximized
696+ readonly property bool maximizedLeft: windowState === WindowStateStorage.WindowStateMaximizedLeft
697+ readonly property bool maximizedRight: windowState === WindowStateStorage.WindowStateMaximizedRight
698+ readonly property bool maximizedHorizontally: windowState === WindowStateStorage.WindowStateMaximizedHorizontally
699+ readonly property bool maximizedVertically: windowState === WindowStateStorage.WindowStateMaximizedVertically
700+ readonly property bool maximizedTopLeft: windowState === WindowStateStorage.WindowStateMaximizedTopLeft
701+ readonly property bool maximizedTopRight: windowState === WindowStateStorage.WindowStateMaximizedTopRight
702+ readonly property bool maximizedBottomLeft: windowState === WindowStateStorage.WindowStateMaximizedBottomLeft
703+ readonly property bool maximizedBottomRight: windowState === WindowStateStorage.WindowStateMaximizedBottomRight
704+ readonly property bool anyMaximized: maximized || maximizedLeft || maximizedRight || maximizedHorizontally || maximizedVertically ||
705+ maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
706+
707+ readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
708+ readonly property bool fullscreen: surface ? surface.state === Mir.FullscreenState : application.fullscreen
709+
710+ readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
711+ readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
712+ (maximumHeight == 0 || maximumHeight >= appContainer.height)
713+ readonly property bool canBeCornerMaximized: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
714+ (maximumHeight == 0 || maximumHeight >= appContainer.height/2)
715+ readonly property bool canBeMaximizedHorizontally: maximumWidth == 0 || maximumWidth >= appContainer.width
716+ readonly property bool canBeMaximizedVertically: maximumHeight == 0 || maximumHeight >= appContainer.height
717+ readonly property alias orientationChangesEnabled: decoratedWindow.orientationChangesEnabled
718+
719+ property int windowState: WindowStateStorage.WindowStateNormal
720+ property bool animationsEnabled: true
721+ property alias title: decoratedWindow.title
722+ readonly property string appName: model.application ? model.application.name : ""
723+ property bool visuallyMaximized: false
724+ property bool visuallyMinimized: false
725+
726+ property int stage: ApplicationInfoInterface.MainStage
727+ function saveStage(newStage) {
728+ appDelegate.stage = newStage;
729+ WindowStateStorage.saveStage(appId, newStage);
730+ priv.updateMainAndSideStageIndexes()
731+ }
732+
733+ readonly property var surface: model.surface
734+ readonly property alias resizeArea: resizeArea
735+ readonly property alias focusedSurface: decoratedWindow.focusedSurface
736+ readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
737+
738+ readonly property string appId: model.application.appId
739+ readonly property bool isDash: appId == "unity8-dash"
740+ readonly property alias clientAreaItem: decoratedWindow.clientAreaItem
741+
742+ function claimFocus() {
743+ if (root.state == "spread") {
744+ spreadItem.highlightedIndex = index
745+ priv.goneToSpread = false;
746+ }
747+ if (root.mode == "stagedWithSideStage") {
748+ if (appDelegate.stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
749+ sideStage.show();
750+ }
751+ priv.updateMainAndSideStageIndexes();
752+ }
753+
754+ if (root.mode == "windowed") {
755+ appDelegate.restore(true /* animated */, appDelegate.windowState);
756+ } else {
757+ appDelegate.focus = true;
758+ }
759+ }
760+ Connections {
761+ target: model.surface
762+ onFocusRequested: {
763+ claimFocus();
764+ }
765+ }
766+ Connections {
767+ target: model.application
768+ onFocusRequested: {
769+ if (!model.surface) {
770+ // when an app has no surfaces, we assume there's only one entry representing it:
771+ // this delegate.
772+ claimFocus();
773+ } else {
774+ // if the application has surfaces, focus request should be at surface-level.
775+ }
776+ }
777+ }
778+
779+ onFocusChanged: {
780+ if (appRepeater.startingUp)
781+ return;
782+
783+ if (focus) {
784+ topLevelSurfaceList.raiseId(model.id);
785+ priv.focusedAppDelegate = appDelegate;
786+ } else if (!focus && priv.focusedAppDelegate === appDelegate && root.state != "spread") {
787+ priv.focusedAppDelegate = null;
788+ // FIXME: No idea why the Binding{} doens't update when focusedAppDelegate turns null
789+ MirFocusController.focusedSurface = null;
790+ }
791+ }
792+ Component.onCompleted: {
793+ if (application && application.rotatesWindowContents) {
794+ decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
795+ } else {
796+ decoratedWindow.surfaceOrientationAngle = 0;
797+ }
798+
799+ // First, cascade the newly created window, relative to the currently/old focused window.
800+ windowedX = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedX + units.gu(3) : (normalZ - 1) * units.gu(3)
801+ windowedY = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedY + units.gu(3) : normalZ * units.gu(3)
802+ // Now load any saved state. This needs to happen *after* the cascading!
803+ resizeArea.loadWindowState();
804+
805+ // NB: We're differentiating if this delegate was created in response to a new entry in the model
806+ // or if the Repeater is just populating itself with delegates to match the model it received.
807+ if (!appRepeater.startingUp) {
808+ // a top level window is always the focused one when it first appears, unfocusing
809+ // any preexisting one
810+ if (root.state == "spread") {
811+ spreadItem.highlightedIndex = index;
812+ }
813+ claimFocus();
814+ }
815+
816+ refreshStage();
817+ _constructing = false;
818+ }
819+ Component.onDestruction: {
820+ if (!root.parent) {
821+ // This stage is about to be destroyed. Don't mess up with the model at this point
822+ return;
823+ }
824+
825+ if (visuallyMaximized) {
826+ priv.updateForegroundMaximizedApp();
827+ }
828+
829+ if (focus) {
830+ // focus some other window
831+ for (var i = 0; i < appRepeater.count; i++) {
832+ var appDelegate = appRepeater.itemAt(i);
833+ if (appDelegate && !appDelegate.minimized && i != index) {
834+ appDelegate.focus = true;
835+ return;
836+ }
837+ }
838+ }
839+ }
840+
841+ onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
842+
843+ property bool _constructing: true;
844+ onStageChanged: {
845+ if (!_constructing) {
846+ priv.updateMainAndSideStageIndexes();
847+ }
848+ }
849+
850+ visible: (
851+ !visuallyMinimized
852+ && !greeter.fullyShown
853+ && (priv.foregroundMaximizedAppDelegate === null || priv.foregroundMaximizedAppDelegate.normalZ <= z)
854+ )
855+ || appDelegate.fullscreen
856+ || focusAnimation.running || rightEdgeFocusAnimation.running || hidingAnimation.running
857+
858+ function close() {
859+ model.surface.close();
860+ }
861+
862+ function maximize(animated) {
863+ animationsEnabled = (animated === undefined) || animated;
864+ windowState = WindowStateStorage.WindowStateMaximized;
865+ }
866+ function maximizeLeft(animated) {
867+ animationsEnabled = (animated === undefined) || animated;
868+ windowState = WindowStateStorage.WindowStateMaximizedLeft;
869+ }
870+ function maximizeRight(animated) {
871+ animationsEnabled = (animated === undefined) || animated;
872+ windowState = WindowStateStorage.WindowStateMaximizedRight;
873+ }
874+ function maximizeHorizontally(animated) {
875+ animationsEnabled = (animated === undefined) || animated;
876+ windowState = WindowStateStorage.WindowStateMaximizedHorizontally;
877+ }
878+ function maximizeVertically(animated) {
879+ animationsEnabled = (animated === undefined) || animated;
880+ windowState = WindowStateStorage.WindowStateMaximizedVertically;
881+ }
882+ function maximizeTopLeft(animated) {
883+ animationsEnabled = (animated === undefined) || animated;
884+ windowState = WindowStateStorage.WindowStateMaximizedTopLeft;
885+ }
886+ function maximizeTopRight(animated) {
887+ animationsEnabled = (animated === undefined) || animated;
888+ windowState = WindowStateStorage.WindowStateMaximizedTopRight;
889+ }
890+ function maximizeBottomLeft(animated) {
891+ animationsEnabled = (animated === undefined) || animated;
892+ windowState = WindowStateStorage.WindowStateMaximizedBottomLeft;
893+ }
894+ function maximizeBottomRight(animated) {
895+ animationsEnabled = (animated === undefined) || animated;
896+ windowState = WindowStateStorage.WindowStateMaximizedBottomRight;
897+ }
898+ function minimize(animated) {
899+ animationsEnabled = (animated === undefined) || animated;
900+ windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
901+ }
902+ function restoreFromMaximized(animated) {
903+ animationsEnabled = (animated === undefined) || animated;
904+ windowState = WindowStateStorage.WindowStateRestored;
905+ }
906+ function restore(animated,state) {
907+ animationsEnabled = (animated === undefined) || animated;
908+ windowState = state || WindowStateStorage.WindowStateRestored;
909+ windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
910+ focus = true;
911+ }
912+
913+ function playFocusAnimation() {
914+ if (state == "stagedRightEdge") {
915+ // TODO: Can we drop this if and find something that always works?
916+ if (root.mode == "staged") {
917+ rightEdgeFocusAnimation.targetX = 0
918+ rightEdgeFocusAnimation.start()
919+ } else if (root.mode == "stagedWithSideStage") {
920+ rightEdgeFocusAnimation.targetX = appDelegate.stage == ApplicationInfoInterface.SideStage ? sideStage.x : 0
921+ rightEdgeFocusAnimation.start()
922+ }
923+ } else if (state == "windowedRightEdge" || state == "windowed") {
924+ claimFocus();
925+ } else {
926+ focusAnimation.start()
927+ }
928+ }
929+ function playHidingAnimation() {
930+ if (state != "windowedRightEdge") {
931+ hidingAnimation.start()
932+ }
933+ }
934+
935+ function refreshStage() {
936+ var newStage = ApplicationInfoInterface.MainStage;
937+ if (priv.sideStageEnabled) { // we're in lanscape rotation.
938+ if (!isDash && application && application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
939+ var defaultStage = ApplicationInfoInterface.SideStage; // if application supports portrait, it defaults to sidestage.
940+ if (application.supportedOrientations & (Qt.LandscapeOrientation|Qt.InvertedLandscapeOrientation)) {
941+ // if it supports lanscape, it defaults to mainstage.
942+ defaultStage = ApplicationInfoInterface.MainStage;
943+ }
944+ newStage = WindowStateStorage.getStage(application.appId, defaultStage);
945+ }
946+ }
947+
948+ stage = newStage;
949+ if (focus && stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
950+ sideStage.show();
951+ }
952+ }
953+
954+ UbuntuNumberAnimation {
955+ id: focusAnimation
956+ target: appDelegate
957+ property: "scale"
958+ from: 0.98
959+ to: 1
960+ duration: UbuntuAnimation.SnapDuration
961+ onStarted: {
962+ topLevelSurfaceList.raiseId(model.id);
963+ }
964+ onStopped: {
965+ appDelegate.claimFocus();
966+ }
967+ }
968+ ParallelAnimation {
969+ id: rightEdgeFocusAnimation
970+ property int targetX: 0
971+ UbuntuNumberAnimation { target: appDelegate; properties: "x"; to: rightEdgeFocusAnimation.targetX; duration: priv.animationDuration }
972+ UbuntuNumberAnimation { target: decoratedWindow; properties: "angle"; to: 0; duration: priv.animationDuration }
973+ UbuntuNumberAnimation { target: decoratedWindow; properties: "itemScale"; to: 1; duration: priv.animationDuration }
974+ onStopped: {
975+ appDelegate.focus = true
976+ }
977+ }
978+ ParallelAnimation {
979+ id: hidingAnimation
980+ UbuntuNumberAnimation { target: appDelegate; property: "opacity"; to: 0; duration: priv.animationDuration }
981+ onStopped: appDelegate.opacity = 1
982+ }
983+
984+ SpreadMaths {
985+ id: spreadMaths
986+ spread: spreadItem
987+ itemIndex: index
988+ flickable: floatingFlickable
989+ }
990+ StageMaths {
991+ id: stageMaths
992+ sceneWidth: root.width
993+ stage: appDelegate.stage
994+ thisDelegate: appDelegate
995+ mainStageDelegate: priv.mainStageDelegate
996+ sideStageDelegate: priv.sideStageDelegate
997+ sideStageWidth: sideStage.panelWidth
998+ sideStageX: sideStage.x
999+ itemIndex: appDelegate.itemIndex
1000+ nextInStack: priv.nextInStack
1001+ leftEdgeDragProgress: root.leftEdgeDragProgress
1002+ }
1003+
1004+ StagedRightEdgeMaths {
1005+ id: stagedRightEdgeMaths
1006+ sceneWidth: appContainer.width - root.leftMargin
1007+ sceneHeight: appContainer.height
1008+ isMainStageApp: priv.mainStageDelegate == appDelegate
1009+ isSideStageApp: priv.sideStageDelegate == appDelegate
1010+ sideStageWidth: sideStage.width
1011+ sideStageOpen: sideStage.shown
1012+ itemIndex: index
1013+ nextInStack: priv.nextInStack
1014+ progress: 0
1015+ targetHeight: spreadItem.stackHeight
1016+ targetX: spreadMaths.targetX
1017+ startY: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1018+ targetY: spreadMaths.targetY
1019+ targetAngle: spreadMaths.targetAngle
1020+ targetScale: spreadMaths.targetScale
1021+ shuffledZ: stageMaths.itemZ
1022+ breakPoint: spreadItem.rightEdgeBreakPoint
1023+ }
1024+
1025+ WindowedRightEdgeMaths {
1026+ id: windowedRightEdgeMaths
1027+ itemIndex: index
1028+ startWidth: appDelegate.requestedWidth
1029+ startHeight: appDelegate.requestedHeight
1030+ targetHeight: spreadItem.stackHeight
1031+ targetX: spreadMaths.targetX
1032+ targetY: spreadMaths.targetY
1033+ normalZ: appDelegate.normalZ
1034+ targetAngle: spreadMaths.targetAngle
1035+ targetScale: spreadMaths.targetScale
1036+ breakPoint: spreadItem.rightEdgeBreakPoint
1037+ }
1038+
1039+ states: [
1040+ State {
1041+ name: "spread"; when: root.state == "spread"
1042+ PropertyChanges {
1043+ target: decoratedWindow;
1044+ showDecoration: false;
1045+ angle: spreadMaths.targetAngle
1046+ itemScale: spreadMaths.targetScale
1047+ scaleToPreviewSize: spreadItem.stackHeight
1048+ scaleToPreviewProgress: 1
1049+ hasDecoration: root.mode === "windowed"
1050+ shadowOpacity: spreadMaths.shadowOpacity
1051+ showHighlight: spreadItem.highlightedIndex === index
1052+ darkening: spreadItem.highlightedIndex >= 0
1053+ anchors.topMargin: dragArea.distance
1054+ }
1055+ PropertyChanges {
1056+ target: appDelegate
1057+ x: spreadMaths.targetX
1058+ y: spreadMaths.targetY
1059+ z: index
1060+ height: spreadItem.spreadItemHeight
1061+ requestedWidth: decoratedWindow.oldRequestedWidth
1062+ requestedHeight: decoratedWindow.oldRequestedHeight
1063+ visible: spreadMaths.itemVisible
1064+ }
1065+ PropertyChanges { target: dragArea; enabled: true }
1066+ PropertyChanges { target: windowInfoItem; opacity: spreadMaths.tileInfoOpacity; visible: spreadMaths.itemVisible }
1067+ },
1068+ State {
1069+ name: "stagedRightEdge"
1070+ when: (root.mode == "staged" || root.mode == "stagedWithSideStage") && (root.state == "sideStagedRightEdge" || root.state == "stagedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running)
1071+ PropertyChanges {
1072+ target: stagedRightEdgeMaths
1073+ progress: Math.max(edgeBarrier.progress, rightEdgeDragArea.draggedProgress)
1074+ }
1075+ PropertyChanges {
1076+ target: appDelegate
1077+ x: stagedRightEdgeMaths.animatedX
1078+ y: stagedRightEdgeMaths.animatedY
1079+ z: stagedRightEdgeMaths.animatedZ
1080+ height: stagedRightEdgeMaths.animatedHeight
1081+ requestedWidth: decoratedWindow.oldRequestedWidth
1082+ requestedHeight: decoratedWindow.oldRequestedHeight
1083+ visible: appDelegate.x < root.width
1084+ }
1085+ PropertyChanges {
1086+ target: decoratedWindow
1087+ hasDecoration: false
1088+ angle: stagedRightEdgeMaths.animatedAngle
1089+ itemScale: stagedRightEdgeMaths.animatedScale
1090+ scaleToPreviewSize: spreadItem.stackHeight
1091+ scaleToPreviewProgress: stagedRightEdgeMaths.scaleToPreviewProgress
1092+ shadowOpacity: .3
1093+ }
1094+ },
1095+ State {
1096+ name: "windowedRightEdge"
1097+ when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running || edgeBarrier.progress > 0)
1098+ PropertyChanges {
1099+ target: windowedRightEdgeMaths
1100+ progress: Math.max(rightEdgeDragArea.progress, edgeBarrier.progress)
1101+ }
1102+ PropertyChanges {
1103+ target: appDelegate
1104+ x: windowedRightEdgeMaths.animatedX
1105+ y: windowedRightEdgeMaths.animatedY
1106+ z: windowedRightEdgeMaths.animatedZ
1107+ height: stagedRightEdgeMaths.animatedHeight
1108+ requestedWidth: decoratedWindow.oldRequestedWidth
1109+ requestedHeight: decoratedWindow.oldRequestedHeight
1110+ }
1111+ PropertyChanges {
1112+ target: decoratedWindow
1113+ showDecoration: windowedRightEdgeMaths.decorationHeight
1114+ angle: windowedRightEdgeMaths.animatedAngle
1115+ itemScale: windowedRightEdgeMaths.animatedScale
1116+ scaleToPreviewSize: spreadItem.stackHeight
1117+ scaleToPreviewProgress: windowedRightEdgeMaths.scaleToPreviewProgress
1118+ shadowOpacity: .3
1119+ }
1120+ PropertyChanges {
1121+ target: opacityEffect;
1122+ opacityValue: windowedRightEdgeMaths.opacityMask
1123+ sourceItem: windowedRightEdgeMaths.opacityMask < 1 ? decoratedWindow : null
1124+ }
1125+ },
1126+ State {
1127+ name: "staged"; when: root.state == "staged"
1128+ PropertyChanges {
1129+ target: appDelegate
1130+ x: stageMaths.itemX
1131+ y: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1132+ requestedWidth: appContainer.width
1133+ requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - PanelState.panelHeight
1134+ visuallyMaximized: true
1135+ visible: appDelegate.x < root.width
1136+ }
1137+ PropertyChanges {
1138+ target: decoratedWindow
1139+ hasDecoration: false
1140+ }
1141+ PropertyChanges {
1142+ target: resizeArea
1143+ enabled: false
1144+ }
1145+ PropertyChanges {
1146+ target: stageMaths
1147+ animateX: !focusAnimation.running && itemIndex !== spreadItem.highlightedIndex
1148+ }
1149+ },
1150+ State {
1151+ name: "stagedWithSideStage"; when: root.state == "stagedWithSideStage"
1152+ PropertyChanges {
1153+ target: stageMaths
1154+ itemIndex: index
1155+ }
1156+ PropertyChanges {
1157+ target: appDelegate
1158+ x: stageMaths.itemX
1159+ y: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1160+ z: stageMaths.itemZ
1161+ requestedWidth: stageMaths.itemWidth
1162+ requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - PanelState.panelHeight
1163+ visuallyMaximized: true
1164+ visible: appDelegate.x < root.width
1165+ }
1166+ PropertyChanges {
1167+ target: decoratedWindow
1168+ hasDecoration: false
1169+ }
1170+ PropertyChanges {
1171+ target: resizeArea
1172+ enabled: false
1173+ }
1174+ },
1175+ State {
1176+ name: "maximized"; when: appDelegate.windowState == WindowStateStorage.WindowStateMaximized
1177+ PropertyChanges {
1178+ target: appDelegate;
1179+ requestedX: root.leftMargin;
1180+ requestedY: 0;
1181+ visuallyMinimized: false;
1182+ visuallyMaximized: true
1183+ requestedWidth: appContainer.width - root.leftMargin;
1184+ requestedHeight: appContainer.height;
1185+ }
1186+ PropertyChanges { target: touchControls; enabled: true }
1187+ },
1188+ State {
1189+ name: "fullscreen"; when: appDelegate.fullscreen && !appDelegate.minimized
1190+ PropertyChanges {
1191+ target: appDelegate;
1192+ requestedX: 0
1193+ requestedY: 0
1194+ requestedWidth: appContainer.width;
1195+ requestedHeight: appContainer.height;
1196+ }
1197+ PropertyChanges { target: decoratedWindow; hasDecoration: false }
1198+ },
1199+ State {
1200+ name: "normal";
1201+ when: appDelegate.windowState == WindowStateStorage.WindowStateNormal
1202+ PropertyChanges {
1203+ target: appDelegate
1204+ visuallyMinimized: false
1205+ visuallyMaximized: false
1206+ }
1207+ PropertyChanges { target: touchControls; enabled: true }
1208+ PropertyChanges { target: resizeArea; enabled: true }
1209+ PropertyChanges { target: decoratedWindow; shadowOpacity: .3}
1210+ },
1211+ State {
1212+ name: "restored";
1213+ when: appDelegate.windowState == WindowStateStorage.WindowStateRestored
1214+ extend: "normal"
1215+ PropertyChanges {
1216+ target: appDelegate;
1217+ windowedX: restoredX;
1218+ windowedY: restoredY;
1219+ }
1220+ },
1221+ State {
1222+ name: "semiMaximized"
1223+ PropertyChanges { target: touchControls; enabled: true }
1224+ PropertyChanges { target: resizeArea; enabled: true }
1225+ PropertyChanges { target: decoratedWindow; shadowOpacity: .3 }
1226+ },
1227+ State {
1228+ name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
1229+ extend: "semiMaximized"
1230+ PropertyChanges {
1231+ target: appDelegate
1232+ windowedX: root.leftMargin
1233+ windowedY: PanelState.panelHeight
1234+ windowedWidth: (appContainer.width - root.leftMargin)/2
1235+ windowedHeight: appContainer.height - PanelState.panelHeight
1236+ }
1237+ },
1238+ State {
1239+ name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
1240+ extend: "maximizedLeft"
1241+ PropertyChanges {
1242+ target: appDelegate;
1243+ windowedX: (appContainer.width + root.leftMargin)/2
1244+ }
1245+ },
1246+ State {
1247+ name: "maximizedTopLeft"; when: appDelegate.maximizedTopLeft && !appDelegate.minimized
1248+ extend: "semiMaximized"
1249+ PropertyChanges {
1250+ target: appDelegate
1251+ windowedX: root.leftMargin
1252+ windowedY: PanelState.panelHeight
1253+ windowedWidth: (appContainer.width - root.leftMargin)/2
1254+ windowedHeight: (appContainer.height - PanelState.panelHeight)/2
1255+ }
1256+ },
1257+ State {
1258+ name: "maximizedTopRight"; when: appDelegate.maximizedTopRight && !appDelegate.minimized
1259+ extend: "maximizedTopLeft"
1260+ PropertyChanges {
1261+ target: appDelegate
1262+ windowedX: (appContainer.width + root.leftMargin)/2
1263+ }
1264+ },
1265+ State {
1266+ name: "maximizedBottomLeft"; when: appDelegate.maximizedBottomLeft && !appDelegate.minimized
1267+ extend: "semiMaximized"
1268+ PropertyChanges {
1269+ target: appDelegate
1270+ windowedX: root.leftMargin
1271+ windowedY: (appContainer.height + PanelState.panelHeight)/2
1272+ windowedWidth: (appContainer.width - root.leftMargin)/2
1273+ windowedHeight: appContainer.height/2
1274+ }
1275+ },
1276+ State {
1277+ name: "maximizedBottomRight"; when: appDelegate.maximizedBottomRight && !appDelegate.minimized
1278+ extend: "maximizedBottomLeft"
1279+ PropertyChanges {
1280+ target: appDelegate
1281+ windowedX: (appContainer.width + root.leftMargin)/2
1282+ }
1283+ },
1284+ State {
1285+ name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
1286+ extend: "semiMaximized"
1287+ PropertyChanges { target: appDelegate; requestedX: root.leftMargin; requestedY: windowedY;
1288+ requestedWidth: appContainer.width - root.leftMargin; requestedHeight: appDelegate.windowedHeight }
1289+ },
1290+ State {
1291+ name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
1292+ extend: "semiMaximized"
1293+ PropertyChanges { target: appDelegate; requestedX: windowedX; requestedY: PanelState.panelHeight;
1294+ requestedWidth: appDelegate.windowedWidth; requestedHeight: appContainer.height - PanelState.panelHeight }
1295+ },
1296+ State {
1297+ name: "minimized"; when: appDelegate.minimized
1298+ PropertyChanges {
1299+ target: appDelegate
1300+ requestedX: -appDelegate.width / 2
1301+ scale: units.gu(5) / appDelegate.width
1302+ opacity: 0;
1303+ visuallyMinimized: true
1304+ visuallyMaximized: false
1305+ }
1306+ }
1307+ ]
1308+ transitions: [
1309+ Transition {
1310+ from: "staged,stagedWithSideStage"; to: "normal"
1311+ enabled: appDelegate.animationsEnabled
1312+ PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
1313+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,opacity,requestedWidth,requestedHeight,scale"; duration: priv.animationDuration }
1314+ },
1315+ Transition {
1316+ from: "normal,maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight";
1317+ to: "staged,stagedWithSideStage"
1318+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,requestedWidth,requestedHeight"; duration: priv.animationDuration}
1319+ },
1320+ Transition {
1321+ from: "maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight,minimized";
1322+ to: "normal,restored"
1323+ enabled: appDelegate.animationsEnabled
1324+ PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
1325+ UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,windowedX,windowedY,requestedWidth,requestedHeight,windowedWidth,windowedHeight,scale";
1326+ duration: priv.animationDuration }
1327+ },
1328+ Transition {
1329+ to: "minimized"
1330+ enabled: appDelegate.animationsEnabled
1331+ PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1332+ SequentialAnimation {
1333+ UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
1334+ PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1335+ ScriptAction {
1336+ script: {
1337+ if (appDelegate.minimized) {
1338+ appDelegate.focus = false;
1339+ priv.focusNext();
1340+ }
1341+ }
1342+ }
1343+ }
1344+ },
1345+ Transition {
1346+ to: "spread"
1347+ // DecoratedWindow wants the scaleToPreviewSize set before enabling scaleToPreview
1348+ PropertyAction { target: appDelegate; properties: "z,visible" }
1349+ PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
1350+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,height"; duration: priv.animationDuration }
1351+ UbuntuNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle,scaleToPreviewProgress"; duration: priv.animationDuration }
1352+ },
1353+ Transition {
1354+ from: "normal,staged"; to: "stagedWithSideStage"
1355+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y"; duration: priv.animationDuration }
1356+ UbuntuNumberAnimation { target: appDelegate; properties: "requestedWidth,requestedHeight"; duration: priv.animationDuration }
1357+ },
1358+ Transition {
1359+ to: "windowedRightEdge"
1360+ ScriptAction {
1361+ script: {
1362+ windowedRightEdgeMaths.startX = appDelegate.requestedX
1363+ windowedRightEdgeMaths.startY = appDelegate.requestedY
1364+
1365+ if (index == 1) {
1366+ var thisRect = { x: appDelegate.windowedX, y: appDelegate.windowedY, width: appDelegate.requestedWidth, height: appDelegate.requestedHeight }
1367+ var otherDelegate = appRepeater.itemAt(0);
1368+ var otherRect = { x: otherDelegate.windowedX, y: otherDelegate.windowedY, width: otherDelegate.requestedWidth, height: otherDelegate.requestedHeight }
1369+ var intersectionRect = MathUtils.intersectionRect(thisRect, otherRect)
1370+ var mappedInterSectionRect = appDelegate.mapFromItem(root, intersectionRect.x, intersectionRect.y)
1371+ opacityEffect.maskX = mappedInterSectionRect.x
1372+ opacityEffect.maskY = mappedInterSectionRect.y
1373+ opacityEffect.maskWidth = intersectionRect.width
1374+ opacityEffect.maskHeight = intersectionRect.height
1375+ }
1376+ }
1377+ }
1378+ },
1379+ Transition {
1380+ from: "stagedRightEdge"; to: "staged"
1381+ enabled: rightEdgeDragArea.cancelled // only transition back to state if the gesture was cancelled, in the other cases we play the focusAnimations.
1382+ SequentialAnimation {
1383+ ParallelAnimation {
1384+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,height,width,scale"; duration: priv.animationDuration }
1385+ UbuntuNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle,scaleToPreviewProgress"; duration: priv.animationDuration }
1386+ }
1387+ // We need to release scaleToPreviewSize at last
1388+ PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
1389+ PropertyAction { target: appDelegate; property: "visible" }
1390+ }
1391+ },
1392+ Transition {
1393+ from: "normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTop,maximizedBottom,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1394+ to: "normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTop,maximizedBottom,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1395+ enabled: appDelegate.animationsEnabled
1396+ SequentialAnimation {
1397+ PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1398+ UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,windowedX,windowedY,opacity,scale,requestedWidth,requestedHeight,windowedWidth,windowedHeight";
1399+ duration: priv.animationDuration }
1400+ PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1401+ ScriptAction { script: { fakeRectangle.stop(); } }
1402+ }
1403+ }
1404+ ]
1405+
1406+ Binding {
1407+ target: PanelState
1408+ property: "buttonsAlwaysVisible"
1409+ value: appDelegate && appDelegate.maximized && touchControls.overlayShown
1410+ }
1411+
1412+ WindowResizeArea {
1413+ id: resizeArea
1414+ objectName: "windowResizeArea"
1415+
1416+ // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
1417+ anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
1418+
1419+ target: appDelegate
1420+ minWidth: units.gu(10)
1421+ minHeight: units.gu(10)
1422+ borderThickness: units.gu(2)
1423+ windowId: model.application.appId // FIXME: Change this to point to windowId once we have such a thing
1424+ screenWidth: appContainer.width
1425+ screenHeight: appContainer.height
1426+ leftMargin: root.leftMargin
1427+ enabled: false
1428+ visible: enabled
1429+
1430+ onPressed: {
1431+ appDelegate.focus = true;
1432+ }
1433+
1434+ Component.onDestruction: {
1435+ saveWindowState();
1436+ }
1437+ }
1438+
1439+ DecoratedWindow {
1440+ id: decoratedWindow
1441+ objectName: "decoratedWindow"
1442+ anchors.left: appDelegate.left
1443+ anchors.top: appDelegate.top
1444+ application: model.application
1445+ surface: model.surface
1446+ active: appDelegate.focus
1447+ focus: true
1448+ interactive: root.interactive
1449+ showDecoration: 1
1450+ maximizeButtonShown: appDelegate.canBeMaximized
1451+ overlayShown: touchControls.overlayShown
1452+ width: implicitWidth
1453+ height: implicitHeight
1454+ highlightSize: windowInfoItem.iconMargin / 2
1455+ stageWidth: appContainer.width
1456+ stageHeight: appContainer.height
1457+
1458+ requestedWidth: appDelegate.requestedWidth
1459+ requestedHeight: appDelegate.requestedHeight
1460+
1461+ property int oldRequestedWidth: -1
1462+ property int oldRequestedHeight: -1
1463+
1464+ onRequestedWidthChanged: oldRequestedWidth = requestedWidth
1465+ onRequestedHeightChanged: oldRequestedHeight = requestedHeight
1466+
1467+ onCloseClicked: { appDelegate.close(); }
1468+ onMaximizeClicked: appDelegate.anyMaximized ? appDelegate.restoreFromMaximized() : appDelegate.maximize();
1469+ onMaximizeHorizontallyClicked: appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
1470+ onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
1471+ onMinimizeClicked: appDelegate.minimize()
1472+ onDecorationPressed: { appDelegate.focus = true; }
1473+ onDecorationReleased: fakeRectangle.commit();
1474+
1475+ property real angle: 0
1476+ property real itemScale: 1
1477+ transform: [
1478+ Scale {
1479+ origin.x: 0
1480+ origin.y: decoratedWindow.implicitHeight / 2
1481+ xScale: decoratedWindow.itemScale
1482+ yScale: decoratedWindow.itemScale
1483+ },
1484+ Rotation {
1485+ origin { x: 0; y: (decoratedWindow.height / 2) }
1486+ axis { x: 0; y: 1; z: 0 }
1487+ angle: decoratedWindow.angle
1488+ }
1489+ ]
1490+ }
1491+
1492+ OpacityMask {
1493+ id: opacityEffect
1494+ anchors.fill: decoratedWindow
1495+ }
1496+
1497+ WindowControlsOverlay {
1498+ id: touchControls
1499+ target: appDelegate
1500+ enabled: false
1501+ visible: enabled
1502+ stageWidth: appContainer.width
1503+ stageHeight: appContainer.height
1504+
1505+ onFakeMaximizeAnimationRequested: if (!appDelegate.maximized) fakeRectangle.maximize(amount, true)
1506+ onFakeMaximizeLeftAnimationRequested: if (!appDelegate.maximizedLeft) fakeRectangle.maximizeLeft(amount, true)
1507+ onFakeMaximizeRightAnimationRequested: if (!appDelegate.maximizedRight) fakeRectangle.maximizeRight(amount, true)
1508+ onFakeMaximizeTopLeftAnimationRequested: if (!appDelegate.maximizedTopLeft) fakeRectangle.maximizeTopLeft(amount, true);
1509+ onFakeMaximizeTopRightAnimationRequested: if (!appDelegate.maximizedTopRight) fakeRectangle.maximizeTopRight(amount, true);
1510+ onFakeMaximizeBottomLeftAnimationRequested: if (!appDelegate.maximizedBottomLeft) fakeRectangle.maximizeBottomLeft(amount, true);
1511+ onFakeMaximizeBottomRightAnimationRequested: if (!appDelegate.maximizedBottomRight) fakeRectangle.maximizeBottomRight(amount, true);
1512+ onStopFakeAnimation: fakeRectangle.stop();
1513+ onDragReleased: fakeRectangle.commit();
1514+ }
1515+
1516+ WindowedFullscreenPolicy {
1517+ id: windowedFullscreenPolicy
1518+ active: root.mode == "windowed"
1519+ surface: model.surface
1520+ }
1521+ StagedFullscreenPolicy {
1522+ id: stagedFullscreenPolicy
1523+ active: root.mode == "staged" || root.mode == "stagedWithSideStage"
1524+ surface: model.surface
1525+ }
1526+
1527+ SpreadDelegateInputArea {
1528+ id: dragArea
1529+ objectName: "dragArea"
1530+ anchors.fill: decoratedWindow
1531+ enabled: false
1532+ closeable: !appDelegate.isDash
1533+
1534+ onClicked: {
1535+ spreadItem.highlightedIndex = index;
1536+ if (distance == 0) {
1537+ priv.goneToSpread = false;
1538+ }
1539+ }
1540+ onClose: {
1541+ priv.closingIndex = index
1542+ if (model.surface) { // could be stopped by OOM
1543+ model.surface.close()
1544+ } else if (model.application) {
1545+ root.applicationManager.stopApplication(model.application.appId);
1546+ }
1547+ }
1548+ }
1549+
1550+ WindowInfoItem {
1551+ id: windowInfoItem
1552+ objectName: "windowInfoItem"
1553+ anchors { left: parent.left; top: decoratedWindow.bottom; topMargin: units.gu(1) }
1554+ title: model.application.name
1555+ iconSource: model.application.icon
1556+ height: spreadItem.appInfoHeight
1557+ opacity: 0
1558+ z: 1
1559+ visible: opacity > 0
1560+ maxWidth: {
1561+ var nextApp = appRepeater.itemAt(index + 1);
1562+ if (nextApp) {
1563+ return nextApp.x - appDelegate.x - units.gu(1)
1564+ }
1565+ return appDelegate.width;
1566+ }
1567+
1568+ onClicked: {
1569+ spreadItem.highlightedIndex = index;
1570+ priv.goneToSpread = false;
1571+ }
1572+ }
1573+
1574+ Image {
1575+ id: closeImage
1576+ anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset }
1577+ source: "graphics/window-close.svg"
1578+ readonly property var mousePos: hoverMouseArea.mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1579+ visible: !appDelegate.isDash && dragArea.distance == 0
1580+ && index == spreadItem.highlightedIndex
1581+ && mousePos.y < (decoratedWindow.height / 3)
1582+ && mousePos.y > -units.gu(4)
1583+ && mousePos.x > -units.gu(4)
1584+ && mousePos.x < (decoratedWindow.width * 2 / 3)
1585+ height: units.gu(2)
1586+ width: height
1587+ sourceSize.width: width
1588+ sourceSize.height: height
1589+
1590+ MouseArea {
1591+ id: closeMouseArea
1592+ objectName: "closeMouseArea"
1593+ anchors.fill: closeImage
1594+ anchors.margins: -units.gu(2)
1595+ onClicked: {
1596+ priv.closingIndex = index;
1597+ appDelegate.close();
1598+ }
1599+ }
1600+ }
1601+ }
1602+ }
1603+ }
1604+
1605+ FakeMaximizeDelegate {
1606+ id: fakeRectangle
1607+ target: priv.focusedAppDelegate
1608+ leftMargin: root.leftMargin
1609+ appContainerWidth: appContainer.width
1610+ appContainerHeight: appContainer.height
1611+ }
1612+
1613+ EdgeBarrier {
1614+ id: edgeBarrier
1615+
1616+ // NB: it does its own positioning according to the specified edge
1617+ edge: Qt.RightEdge
1618+
1619+ onPassed: priv.goneToSpread = true;
1620+
1621+ material: Component {
1622+ Item {
1623+ Rectangle {
1624+ width: parent.height
1625+ height: parent.width
1626+ rotation: 90
1627+ anchors.centerIn: parent
1628+ gradient: Gradient {
1629+ GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
1630+ GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
1631+ }
1632+ }
1633+ }
1634+ }
1635+ }
1636+
1637+ MouseArea {
1638+ id: hoverMouseArea
1639+ objectName: "hoverMouseArea"
1640+ anchors.fill: appContainer
1641+ propagateComposedEvents: true
1642+ hoverEnabled: true
1643+ enabled: false
1644+ visible: enabled
1645+
1646+ property int scrollAreaWidth: width / 3
1647+ property bool progressiveScrollingEnabled: false
1648+
1649+ onMouseXChanged: {
1650+ mouse.accepted = false
1651+
1652+ if (hoverMouseArea.pressed) {
1653+ return;
1654+ }
1655+
1656+ // Find the hovered item and mark it active
1657+ var mapped = mapToItem(appContainer, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1658+ var itemUnder = appContainer.childAt(mapped.x, mapped.y)
1659+ if (itemUnder) {
1660+ mapped = mapToItem(itemUnder, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1661+ var delegateChild = itemUnder.childAt(mapped.x, mapped.y)
1662+ if (delegateChild && (delegateChild.objectName === "dragArea" || delegateChild.objectName === "windowInfoItem")) {
1663+ spreadItem.highlightedIndex = appRepeater.indexOf(itemUnder)
1664+ }
1665+ }
1666+
1667+ if (floatingFlickable.contentWidth > floatingFlickable.width) {
1668+ var margins = floatingFlickable.width * 0.05;
1669+
1670+ if (!progressiveScrollingEnabled && mouseX < floatingFlickable.width - scrollAreaWidth) {
1671+ progressiveScrollingEnabled = true
1672+ }
1673+
1674+ // do we need to scroll?
1675+ if (mouseX < scrollAreaWidth + margins) {
1676+ var progress = Math.min(1, (scrollAreaWidth + margins - mouseX) / (scrollAreaWidth - margins));
1677+ var contentX = (1 - progress) * (floatingFlickable.contentWidth - floatingFlickable.width)
1678+ floatingFlickable.contentX = Math.max(0, Math.min(floatingFlickable.contentX, contentX))
1679+ }
1680+ if (mouseX > floatingFlickable.width - scrollAreaWidth && progressiveScrollingEnabled) {
1681+ var progress = Math.min(1, (mouseX - (floatingFlickable.width - scrollAreaWidth)) / (scrollAreaWidth - margins))
1682+ var contentX = progress * (floatingFlickable.contentWidth - floatingFlickable.width)
1683+ floatingFlickable.contentX = Math.min(floatingFlickable.contentWidth - floatingFlickable.width, Math.max(floatingFlickable.contentX, contentX))
1684+ }
1685+ }
1686+ }
1687+
1688+ onPressed: mouse.accepted = false
1689+ }
1690+
1691+ FloatingFlickable {
1692+ id: floatingFlickable
1693+ objectName: "spreadFlickable"
1694+ anchors.fill: appContainer
1695+ enabled: false
1696+ contentWidth: spreadItem.spreadTotalWidth
1697+
1698+ function snap(toIndex) {
1699+ var delegate = appRepeater.itemAt(toIndex)
1700+ var targetContentX = floatingFlickable.contentWidth / spreadItem.totalItemCount * toIndex;
1701+ if (targetContentX - floatingFlickable.contentX > spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) {
1702+ var offset = (spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) - (targetContentX - floatingFlickable.contentX)
1703+ snapAnimation.to = floatingFlickable.contentX - offset;
1704+ snapAnimation.start();
1705+ } else if (targetContentX - floatingFlickable.contentX < spreadItem.leftStackXPos + units.gu(1)) {
1706+ var offset = (spreadItem.leftStackXPos + units.gu(1)) - (targetContentX - floatingFlickable.contentX);
1707+ snapAnimation.to = floatingFlickable.contentX - offset;
1708+ snapAnimation.start();
1709+ }
1710+ }
1711+ UbuntuNumberAnimation {id: snapAnimation; target: floatingFlickable; property: "contentX"}
1712+ }
1713+
1714+ PropertyAnimation {
1715+ id: shortRightEdgeSwipeAnimation
1716+ property: "x"
1717+ to: 0
1718+ duration: priv.animationDuration
1719+ }
1720+
1721+ SwipeArea {
1722+ id: rightEdgeDragArea
1723+ objectName: "rightEdgeDragArea"
1724+ direction: Direction.Leftwards
1725+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
1726+ width: root.dragAreaWidth
1727+
1728+ property var gesturePoints: []
1729+ property bool cancelled: false
1730+
1731+ property real progress: dragging ? -touchPosition.x / root.width : 0
1732+ onProgressChanged: {
1733+ if (dragging) {
1734+ draggedProgress = progress;
1735+ }
1736+ }
1737+
1738+ property real draggedProgress: 0
1739+
1740+ onTouchPositionChanged: {
1741+ gesturePoints.push(touchPosition.x);
1742+ if (gesturePoints.length > 10) {
1743+ gesturePoints.splice(0, gesturePoints.length - 10)
1744+ }
1745+ }
1746+
1747+ onDraggingChanged: {
1748+ if (dragging) {
1749+ // A potential edge-drag gesture has started. Start recording it
1750+ gesturePoints = [];
1751+ cancelled = false;
1752+ draggedProgress = 0;
1753+ } else {
1754+ // Ok. The user released. Did he drag far enough to go to full spread?
1755+ if (gesturePoints[gesturePoints.length - 1] < -spreadItem.rightEdgeBreakPoint * spreadItem.width ) {
1756+
1757+ // He dragged far enough, but if the last movement was a flick to the right again, he wants to cancel the spread again.
1758+ var oneWayFlickToRight = true;
1759+ var smallestX = gesturePoints[0]-1;
1760+ for (var i = 0; i < gesturePoints.length; i++) {
1761+ if (gesturePoints[i] <= smallestX) {
1762+ oneWayFlickToRight = false;
1763+ break;
1764+ }
1765+ smallestX = gesturePoints[i];
1766+ }
1767+
1768+ if (!oneWayFlickToRight) {
1769+ // Ok, the user made it, let's go to spread!
1770+ priv.goneToSpread = true;
1771+ } else {
1772+ cancelled = true;
1773+ }
1774+ } else {
1775+ // Ok, the user didn't drag far enough to cross the breakPoint
1776+ // Find out if it was a one-way movement to the left, in which case we just switch directly to next app.
1777+ var oneWayFlick = true;
1778+ var smallestX = rightEdgeDragArea.width;
1779+ for (var i = 0; i < gesturePoints.length; i++) {
1780+ if (gesturePoints[i] >= smallestX) {
1781+ oneWayFlick = false;
1782+ break;
1783+ }
1784+ smallestX = gesturePoints[i];
1785+ }
1786+
1787+ if (appRepeater.count > 1 &&
1788+ (oneWayFlick && rightEdgeDragArea.distance > units.gu(2) || rightEdgeDragArea.distance > spreadItem.rightEdgeBreakPoint * spreadItem.width)) {
1789+ var nextStage = appRepeater.itemAt(priv.nextInStack).stage
1790+ for (var i = 0; i < appRepeater.count; i++) {
1791+ if (i != priv.nextInStack && appRepeater.itemAt(i).stage == nextStage) {
1792+ appRepeater.itemAt(i).playHidingAnimation()
1793+ break;
1794+ }
1795+ }
1796+ appRepeater.itemAt(priv.nextInStack).playFocusAnimation()
1797+ if (appRepeater.itemAt(priv.nextInStack).stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
1798+ sideStage.show();
1799+ }
1800+
1801+ } else {
1802+ cancelled = true;
1803+ }
1804+
1805+ gesturePoints = [];
1806+ }
1807+ }
1808+ }
1809+ }
1810+
1811+ TabletSideStageTouchGesture {
1812+ id: triGestureArea
1813+ objectName: "triGestureArea"
1814+ anchors.fill: parent
1815+ enabled: false
1816+ property Item appDelegate
1817+
1818+ dragComponent: dragComponent
1819+ dragComponentProperties: { "appDelegate": appDelegate }
1820+
1821+ onPressed: {
1822+ function matchDelegate(obj) { return String(obj.objectName).indexOf("appDelegate") >= 0; }
1823+
1824+ var delegateAtCenter = Functions.itemAt(appContainer, x, y, matchDelegate);
1825+ if (!delegateAtCenter) return;
1826+
1827+ appDelegate = delegateAtCenter;
1828+ }
1829+
1830+ onClicked: {
1831+ if (sideStage.shown) {
1832+ sideStage.hide();
1833+ } else {
1834+ sideStage.show();
1835+ priv.updateMainAndSideStageIndexes()
1836+ }
1837+ }
1838+
1839+ onDragStarted: {
1840+ // If we're dragging to the sidestage.
1841+ if (!sideStage.shown) {
1842+ sideStage.show();
1843+ }
1844+ }
1845+
1846+ Component {
1847+ id: dragComponent
1848+ SurfaceContainer {
1849+ property Item appDelegate
1850+
1851+ surface: appDelegate ? appDelegate.surface : null
1852+
1853+ consumesInput: false
1854+ interactive: false
1855+ focus: false
1856+ requestedWidth: appDelegate.requestedWidth
1857+ requestedHeight: appDelegate.requestedHeight
1858+
1859+ width: units.gu(40)
1860+ height: units.gu(40)
1861+
1862+ Drag.hotSpot.x: width/2
1863+ Drag.hotSpot.y: height/2
1864+ // only accept opposite stage.
1865+ Drag.keys: {
1866+ if (!surface) return "Disabled";
1867+ if (appDelegate.isDash) return "Disabled";
1868+
1869+ if (appDelegate.stage === ApplicationInfo.MainStage) {
1870+ if (appDelegate.application.supportedOrientations
1871+ & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
1872+ return "MainStage";
1873+ }
1874+ return "Disabled";
1875+ }
1876+ return "SideStage";
1877+ }
1878+ }
1879+ }
1880+ }
1881+}
qml/Stage/StageMaths.qmlnew file+79
@@ -0,0 +1,79 @@
1+import QtQuick 2.4
2+import Unity.Application 0.1
3+import Ubuntu.Components 1.3
4+
5+QtObject {
6+ id: root
7+
8+ // input
9+ property int itemIndex: 0
10+ property int nextInStack: 0
11+ property int sceneWidth: 0
12+ property int sideStageWidth: 0
13+ property int sideStageX: sceneWidth
14+ property bool animateX: false
15+ property int leftEdgeDragProgress: 0
16+
17+ property int stage: ApplicationInfoInterface.MainStage
18+ property var thisDelegate: null
19+ property var mainStageDelegate: null
20+ property var sideStageDelegate: null
21+
22+ // output
23+
24+ // We need to shuffle z ordering a bit in order to keep side stage apps above main stage apps.
25+ // We don't want to really reorder them in the model because that allows us to keep track
26+ // of the last focused order.
27+ readonly property int itemZ: {
28+ // only shuffle when we've got a main and side stage
29+ if (thisDelegate.isDash && thisDelegate != mainStageDelegate && leftEdgeDragProgress > 0) {
30+ return -1; // Keep the dash behind all other apps for the left edge gesture
31+ }
32+
33+ if (!sideStageDelegate) return itemIndex;
34+
35+ // don't shuffle indexes greater than "actives or next"
36+ if (itemIndex > 2) return itemIndex;
37+
38+ if (thisDelegate == mainStageDelegate) {
39+ // Active main stage always at 0
40+ return 0;
41+ }
42+
43+ if (nextInStack > 0) {
44+ var stageOfNextInStack = appRepeater.itemAt(nextInStack).stage;
45+
46+ if (itemIndex === nextInStack) {
47+ // this is the next app in stack.
48+
49+ if (stage === ApplicationInfoInterface.SideStage) {
50+ // if the next app in stack is a sidestage app, it must order on top of other side stage app
51+ return Math.min(2, topLevelSurfaceList.count-1);
52+ }
53+ return 1;
54+ }
55+ if (stageOfNextInStack === ApplicationInfoInterface.SideStage) {
56+ // if the next app in stack is a sidestage app, it must order on top of other side stage app
57+ return 1;
58+ }
59+ return Math.min(2, topLevelSurfaceList.count-1);
60+ }
61+ return Math.min(index+1, topLevelSurfaceList.count-1);
62+ }
63+
64+
65+ property int itemX: {
66+ if (mainStageDelegate == thisDelegate) {
67+ return thisDelegate.isDash ? 0 : leftEdgeDragProgress;
68+ }
69+ if (sideStageDelegate == thisDelegate) {
70+ return sideStageX;
71+ }
72+ return thisDelegate.isDash && leftEdgeDragProgress > 0 ? 0 : sceneWidth;
73+ }
74+ Behavior on itemX { enabled: root.animateX; UbuntuNumberAnimation {} }
75+
76+ readonly property int itemWidth: stage == ApplicationInfoInterface.MainStage ?
77+ sideStageDelegate != null ? sideStageX : sceneWidth :
78+ stage == ApplicationInfoInterface.SideStage ? sideStageWidth : sceneWidth
79+}
qml/Stages/StagedFullscreenPolicy.qml → qml/Stage/StagedFullscreenPolicy.qmlrenamed

The content is the same.

qml/Stages/SurfaceContainer.qml → qml/Stage/SurfaceContainer.qmlrenamed+5−52
@@ -23,6 +23,8 @@ import Unity.Application 0.1 // for MirSurfaceItem
2323FocusScope {
2424 id: root
2525 objectName: "surfaceContainer"
26+ implicitWidth: surfaceItem.implicitWidth
27+ implicitHeight: surfaceItem.implicitHeight
2628
2729 // Must be set from outside
2830 property var surface: null
@@ -32,7 +34,6 @@ FocusScope {
3234 property int requestedHeight: -1
3335 property bool interactive
3436 property int surfaceOrientationAngle: 0
35− property bool resizeSurface: true
3637 property bool isPromptSurface: false
3738 // FIME - dont export, use interactive property. Need to fix qtmir to handle consumesInputChanged
3839 // to update surface activeFocus. See mock MirSurfaceItem.
@@ -58,35 +59,15 @@ FocusScope {
5859 MirSurfaceItem {
5960 id: surfaceItem
6061 objectName: "surfaceItem"
62+ anchors.fill: parent
6163
6264 focus: true
6365
6466 fillMode: MirSurfaceItem.PadOrCrop
6567 consumesInput: true
6668
67− surfaceWidth: {
68− if (root.resizeSurface) {
69− if (root.requestedWidth >= 0) {
70− return root.requestedWidth;
71− } else {
72− return width;
73− }
74− } else {
75− return -1;
76− }
77− }
78−
79− surfaceHeight: {
80− if (root.resizeSurface) {
81− if (root.requestedHeight >= 0) {
82− return root.requestedHeight;
83− } else {
84− return height;
85− }
86− } else {
87− return -1;
88− }
89− }
69+ surfaceWidth: root.requestedWidth
70+ surfaceHeight: root.requestedHeight
9071
9172 enabled: root.interactive
9273 antialiasing: !root.interactive
@@ -99,34 +80,6 @@ FocusScope {
9980 enabled: surfaceItem.enabled
10081 }
10182
102− // MirSurface size drives SurfaceContainer size
103− Binding {
104− target: surfaceItem; property: "width"; value: root.surface ? root.surface.size.width : 0
105− when: root.requestedWidth >= 0 && root.surface
106− }
107− Binding {
108− target: surfaceItem; property: "height"; value: root.surface ? root.surface.size.height : 0
109− when: root.requestedHeight >= 0 && root.surface
110− }
111− Binding {
112− target: root; property: "width"; value: surfaceItem.width
113− when: root.requestedWidth >= 0
114− }
115− Binding {
116− target: root; property: "height"; value: surfaceItem.height
117− when: root.requestedHeight >= 0
118− }
119−
120− // SurfaceContainer size drives MirSurface size
121− Binding {
122− target: surfaceItem; property: "width"; value: root.width
123− when: root.requestedWidth < 0
124− }
125− Binding {
126− target: surfaceItem; property: "height"; value: root.height
127− when: root.requestedHeight < 0
128− }
129−
13083 Loader {
13184 id: animationsLoader
13285 objectName: "animationsLoader"
qml/Stages/TabletSideStageTouchGesture.qml → qml/Stage/TabletSideStageTouchGesture.qmlrenamed

The content is the same.

qml/Stages/TabletStage.qml → qml/Stage/TabletStage.qmlrenamed

The content is the same.

qml/Stages/TopLevelSurfaceRepeater.qml → qml/Stage/TopLevelSurfaceRepeater.qmlrenamed+9
@@ -55,4 +55,13 @@ Repeater {
5555 startingUp = false;
5656 }
5757 }
58+
59+ function indexOf(delegateItem) {
60+ for (var i = 0; i < count; i++) {
61+ if (itemAt(i) === delegateItem) {
62+ return i;
63+ }
64+ }
65+ return -1;
66+ }
5867}
qml/Stages/TransformedSpreadDelegate.qml → qml/Stage/TransformedSpreadDelegate.qmlrenamed

The content is the same.

qml/Stages/TransformedTabletSpreadDelegate.qml → qml/Stage/TransformedTabletSpreadDelegate.qmlrenamed

The content is the same.

qml/Stages/WindowControlsOverlay.qml → qml/Stage/WindowControlsOverlay.qmlrenamed

The content is the same.

qml/Stages/WindowDecoration.qml → qml/Stage/WindowDecoration.qmlrenamed+1−1
@@ -79,7 +79,7 @@ MouseArea {
7979 onMaximizeClicked: root.maximizeClicked();
8080 onMaximizeHorizontallyClicked: if (root.target.canBeMaximizedHorizontally) root.maximizeHorizontallyClicked();
8181 onMaximizeVerticallyClicked: if (root.target.canBeMaximizedVertically) root.maximizeVerticallyClicked();
82− closeButtonShown: root.target.application.appId !== "unity8-dash"
82+ closeButtonShown: root.target.appId !== "unity8-dash"
8383 }
8484
8585 Label {
qml/Stage/WindowInfoItem.qmlnew file+53
@@ -0,0 +1,53 @@
1+import QtQuick 2.4
2+import Ubuntu.Components 1.3
3+
4+Item {
5+ id: root
6+ implicitWidth: Math.max(iconShape.width, titleLabel.width)
7+ implicitHeight: iconShape.height + titleLabel.height + labelMargin + iconMargin
8+ property alias title: titleLabel.text
9+ property alias iconSource: icon.source
10+
11+ property real iconHeight: (height - titleLabel.height) * 0.65
12+ property real iconMargin: (height - titleLabel.height) * 0.25
13+ property real labelMargin: (height - titleLabel.height) * 0.1
14+ property int maxWidth: units.gu(10)
15+
16+ signal clicked()
17+
18+ ProportionalShape {
19+ id: iconShape
20+ anchors {
21+ top: parent.top
22+ topMargin: iconMargin
23+ left: parent.left
24+ }
25+ height: iconHeight
26+ borderSource: "undefined"
27+ aspect: UbuntuShape.Flat
28+ source: Image {
29+ id: icon
30+ sourceSize.width: iconShape.width
31+ sourceSize.height: iconShape.height
32+ cache: false // see lpbug#1543290 why no cache
33+ }
34+ }
35+
36+ MouseArea {
37+ anchors.fill: iconShape
38+ onClicked: root.clicked()
39+ }
40+
41+ Label {
42+ id: titleLabel
43+ anchors {
44+ left: iconShape.left
45+ top: iconShape.bottom
46+ topMargin: labelMargin
47+ }
48+ width: root.maxWidth
49+ fontSize: 'small'
50+ color: 'white'
51+ elide: Label.ElideRight
52+ }
53+}
qml/Stages/WindowResizeArea.qml → qml/Stage/WindowResizeArea.qmlrenamed+32−31
@@ -75,14 +75,15 @@ MouseArea {
7575
7676 function loadWindowState() {
7777 var windowGeometry = windowStateStorage.getGeometry(root.windowId,
78− Qt.rect(target.requestedX, target.requestedY, defaultWidth, defaultHeight));
78+ Qt.rect(target.windowedX, target.windowedY, defaultWidth, defaultHeight));
7979
80− target.requestedWidth = Qt.binding(function() { return Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth - root.leftMargin); });
81− target.requestedHeight = Qt.binding(function() { return Math.min(Math.max(windowGeometry.height, d.minimumHeight),
80+
81+ target.windowedWidth = Qt.binding(function() { return Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth - root.leftMargin); });
82+ target.windowedHeight = Qt.binding(function() { return Math.min(Math.max(windowGeometry.height, d.minimumHeight),
8283 root.screenHeight - (target.fullscreen ? 0 : PanelState.panelHeight)); });
83− target.requestedX = Qt.binding(function() { return Math.max(Math.min(windowGeometry.x, root.screenWidth - root.leftMargin - target.requestedWidth),
84+ target.windowedX = Qt.binding(function() { return Math.max(Math.min(windowGeometry.x, root.screenWidth - root.leftMargin - target.windowedWidth),
8485 (target.fullscreen ? 0 : root.leftMargin)); });
85− target.requestedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight); });
86+ target.windowedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.windowedHeight), PanelState.panelHeight); });
8687
8788 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
8889 target.restore(false /* animated */, windowState);
@@ -111,28 +112,28 @@ MouseArea {
111112
112113 readonly property int minimumWidth: root.target ? Math.max(root.minWidth, root.target.minimumWidth) : root.minWidth
113114 onMinimumWidthChanged: {
114− if (target.requestedWidth < minimumWidth) {
115− target.requestedWidth = minimumWidth;
115+ if (target.windowedWidth < minimumWidth) {
116+ target.windowedWidth = minimumWidth;
116117 }
117118 }
118119 readonly property int minimumHeight: root.target ? Math.max(root.minHeight, root.target.minimumHeight) : root.minHeight
119120 onMinimumHeightChanged: {
120− if (target.requestedHeight < minimumHeight) {
121− target.requestedHeight = minimumHeight;
121+ if (target.windowedHeight < minimumHeight) {
122+ target.windowedHeight = minimumHeight;
122123 }
123124 }
124125 readonly property int maximumWidth: root.target && root.target.maximumWidth >= minimumWidth && root.target.maximumWidth > 0
125126 ? root.target.maximumWidth : maxSafeInt
126127 onMaximumWidthChanged: {
127− if (target.requestedWidth > maximumWidth) {
128− target.requestedWidth = maximumWidth;
128+ if (target.windowedWidth > maximumWidth) {
129+ target.windowedWidth = maximumWidth;
129130 }
130131 }
131132 readonly property int maximumHeight: root.target && root.target.maximumHeight >= minimumHeight && root.target.maximumHeight > 0
132133 ? root.target.maximumHeight : maxSafeInt
133134 onMaximumHeightChanged: {
134− if (target.requestedHeight > maximumHeight) {
135− target.requestedHeight = maximumHeight;
135+ if (target.windowedHeight > maximumHeight) {
136+ target.windowedHeight = maximumHeight;
136137 }
137138 }
138139 readonly property int widthIncrement: {
@@ -247,8 +248,8 @@ MouseArea {
247248 var pos = mapToItem(root.target.parent, mouseX, mouseY);
248249 d.startMousePosX = pos.x;
249250 d.startMousePosY = pos.y;
250− d.startX = target.requestedX;
251− d.startY = target.requestedY;
251+ d.startX = target.windowedX;
252+ d.startY = target.windowedY;
252253 d.startWidth = target.width;
253254 d.startHeight = target.height;
254255 d.currentWidth = target.width;
@@ -285,53 +286,53 @@ MouseArea {
285286
286287 if (d.leftBorder) {
287288 var newTargetX = d.startX + deltaX;
288− var rightBorderX = target.requestedX + target.width;
289+ var rightBorderX = target.windowedX + target.width;
289290 if (rightBorderX > newTargetX + d.minimumWidth) {
290291 if (rightBorderX < newTargetX + d.maximumWidth) {
291− target.requestedWidth = rightBorderX - newTargetX;
292+ target.windowedWidth = rightBorderX - newTargetX;
292293 } else {
293− target.requestedWidth = d.maximumWidth;
294+ target.windowedWidth = d.maximumWidth;
294295 }
295296 } else {
296− target.requestedWidth = d.minimumWidth;
297+ target.windowedWidth = d.minimumWidth;
297298 }
298299
299300 } else if (d.rightBorder) {
300301 var newWidth = d.startWidth + deltaX;
301302 if (newWidth > d.minimumWidth) {
302303 if (newWidth < d.maximumWidth) {
303− target.requestedWidth = newWidth;
304+ target.windowedWidth = newWidth;
304305 } else {
305− target.requestedWidth = d.maximumWidth;
306+ target.windowedWidth = d.maximumWidth;
306307 }
307308 } else {
308− target.requestedWidth = d.minimumWidth;
309+ target.windowedWidth = d.minimumWidth;
309310 }
310311 }
311312
312313 if (d.topBorder) {
313314 var newTargetY = Math.max(d.startY + deltaY, PanelState.panelHeight); // disallow resizing up past Panel
314− var bottomBorderY = target.requestedY + target.height;
315+ var bottomBorderY = target.windowedY + target.height;
315316 if (bottomBorderY > newTargetY + d.minimumHeight) {
316317 if (bottomBorderY < newTargetY + d.maximumHeight) {
317− target.requestedHeight = bottomBorderY - newTargetY;
318+ target.windowedHeight = bottomBorderY - newTargetY;
318319 } else {
319− target.requestedHeight = d.maximumHeight;
320+ target.windowedHeight = d.maximumHeight;
320321 }
321322 } else {
322− target.requestedHeight = d.minimumHeight;
323+ target.windowedHeight = d.minimumHeight;
323324 }
324325
325326 } else if (d.bottomBorder) {
326327 var newHeight = d.startHeight + deltaY;
327328 if (newHeight > d.minimumHeight) {
328329 if (newHeight < d.maximumHeight) {
329− target.requestedHeight = newHeight;
330+ target.windowedHeight = newHeight;
330331 } else {
331− target.requestedHeight = d.maximumHeight;
332+ target.windowedHeight = d.maximumHeight;
332333 }
333334 } else {
334− target.requestedHeight = d.minimumHeight;
335+ target.windowedHeight = d.minimumHeight;
335336 }
336337 }
337338 }
@@ -340,13 +341,13 @@ MouseArea {
340341 target: root.target
341342 onWidthChanged: {
342343 if (d.moveLeftBorder) {
343− target.requestedX += d.currentWidth - target.width;
344+ target.windowedX += d.currentWidth - target.width;
344345 }
345346 d.currentWidth = target.width;
346347 }
347348 onHeightChanged: {
348349 if (d.moveTopBorder) {
349− target.requestedY += d.currentHeight - target.height;
350+ target.windowedY += d.currentHeight - target.height;
350351 }
351352 d.currentHeight = target.height;
352353 }
qml/Stages/WindowedFullscreenPolicy.qml → qml/Stage/WindowedFullscreenPolicy.qmlrenamed

The content is the same.

qml/Stages/graphics/PageHeaderBaseDividerBottom@18.png → qml/Stage/graphics/PageHeaderBaseDividerBottom@18.pngrenamed

Binary file not shown.

qml/Stages/graphics/PageHeaderBaseDividerLight@18.png → qml/Stage/graphics/PageHeaderBaseDividerLight@18.pngrenamed

Binary file not shown.

qml/Stages/graphics/PageHeaderBaseDividerLight@18.sci → qml/Stage/graphics/PageHeaderBaseDividerLight@18.scirenamed

The content is the same.

qml/Stages/graphics/arrows-centre.png → qml/Stage/graphics/arrows-centre.pngrenamed

Binary file not shown.

qml/Stages/graphics/arrows.png → qml/Stage/graphics/arrows.pngrenamed

Binary file not shown.

qml/Stages/graphics/sidestage_drag.svg → qml/Stage/graphics/sidestage_drag.svgrenamed

The content is the same.

qml/Stages/graphics/sidestage_handle@20.png → qml/Stage/graphics/sidestage_handle@20.pngrenamed

Binary file not shown.

qml/Stages/graphics/sidestage_handle@20.sci → qml/Stage/graphics/sidestage_handle@20.scirenamed

The content is the same.

qml/Stages/graphics/sidestage_open.svg → qml/Stage/graphics/sidestage_open.svgrenamed

The content is the same.

qml/Stages/graphics/window-close.svg → qml/Stage/graphics/window-close.svgrenamed

The content is the same.

qml/Stages/DecoratedWindow.qmldeleted−157
@@ -1,157 +0,0 @@
1−/*
2− * Copyright (C) 2014-2016 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−import QtQuick 2.4
18−import Ubuntu.Components 1.3
19−import Unity.Application 0.1
20−
21−FocusScope {
22− id: root
23−
24− width: !counterRotate ? applicationWindow.width : applicationWindow.height
25− height: visibleDecorationHeight + (!counterRotate ? applicationWindow.height : applicationWindow.width)
26−
27− property alias application: applicationWindow.application
28− property alias surface: applicationWindow.surface
29− readonly property alias focusedSurface: applicationWindow.focusedSurface
30− property alias active: decoration.active
31− readonly property alias title: applicationWindow.title
32− property alias fullscreen: applicationWindow.fullscreen
33− property alias maximizeButtonShown: decoration.maximizeButtonShown
34−
35− readonly property bool decorationShown: !fullscreen
36− property bool highlightShown: false
37− property real shadowOpacity: 1
38−
39− property real requestedWidth
40− property real requestedHeight
41−
42− property alias surfaceOrientationAngle: applicationWindow.surfaceOrientationAngle
43− readonly property real visibleDecorationHeight: root.decorationShown ? decoration.height : 0
44− readonly property bool counterRotate: surfaceOrientationAngle != 0 && surfaceOrientationAngle != 180
45−
46− readonly property int minimumWidth: !counterRotate ? applicationWindow.minimumWidth : applicationWindow.minimumHeight
47− readonly property int minimumHeight: visibleDecorationHeight + (!counterRotate ? applicationWindow.minimumHeight : applicationWindow.minimumWidth)
48− readonly property int maximumWidth: !counterRotate ? applicationWindow.maximumWidth : applicationWindow.maximumHeight
49− readonly property int maximumHeight: (root.decorationShown && applicationWindow.maximumHeight > 0 ? decoration.height : 0)
50− + (!counterRotate ? applicationWindow.maximumHeight : applicationWindow.maximumWidth)
51− readonly property int widthIncrement: !counterRotate ? applicationWindow.widthIncrement : applicationWindow.heightIncrement
52− readonly property int heightIncrement: !counterRotate ? applicationWindow.heightIncrement : applicationWindow.widthIncrement
53−
54− property alias overlayShown: decoration.overlayShown
55− property alias stageWidth: moveHandler.stageWidth
56− property alias stageHeight: moveHandler.stageHeight
57− readonly property alias dragging: moveHandler.dragging
58−
59− readonly property Item clientAreaItem: applicationWindow
60−
61− signal closeClicked()
62− signal maximizeClicked()
63− signal maximizeHorizontallyClicked()
64− signal maximizeVerticallyClicked()
65− signal minimizeClicked()
66− signal decorationPressed()
67− signal decorationReleased()
68−
69− Rectangle {
70− id: selectionHighlight
71− anchors.fill: parent
72− anchors.margins: -units.gu(1)
73− color: "white"
74− opacity: highlightShown ? 0.15 : 0
75− }
76−
77− Rectangle {
78− anchors { left: selectionHighlight.left; right: selectionHighlight.right; bottom: selectionHighlight.bottom; }
79− height: units.dp(2)
80− color: theme.palette.normal.focus
81− visible: highlightShown
82− }
83−
84− BorderImage {
85− anchors {
86− fill: root
87− margins: active ? -units.gu(2) : -units.gu(1.5)
88− }
89− source: "graphics/dropshadow2gu.sci"
90− opacity: root.shadowOpacity * .3
91− visible: !fullscreen
92− }
93−
94− WindowDecoration {
95− id: decoration
96− target: root.parent
97− objectName: "appWindowDecoration"
98− anchors { left: parent.left; top: parent.top; right: parent.right }
99− height: units.gu(3)
100− width: root.width
101− title: applicationWindow.title
102− visible: root.decorationShown
103−
104− onCloseClicked: root.closeClicked();
105− onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); }
106− onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); }
107− onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }
108− onMinimizeClicked: root.minimizeClicked();
109− onPressed: root.decorationPressed();
110−
111− onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
112− onPositionChanged: moveHandler.handlePositionChanged(mouse)
113− onReleased: {
114− root.decorationReleased();
115− moveHandler.handleReleased();
116− }
117− }
118−
119− MoveHandler {
120− id: moveHandler
121− objectName: "moveHandler"
122− target: root.parent
123− buttonsWidth: decoration.buttonsWidth
124− }
125−
126− ApplicationWindow {
127− id: applicationWindow
128− objectName: "appWindow"
129− anchors.top: parent.top
130− anchors.topMargin: decoration.height
131− anchors.left: parent.left
132− readonly property real requestedHeightMinusDecoration: root.requestedHeight - root.visibleDecorationHeight
133− requestedHeight: !counterRotate ? requestedHeightMinusDecoration : root.requestedWidth
134− requestedWidth: !counterRotate ? root.requestedWidth : requestedHeightMinusDecoration
135− interactive: true
136− focus: true
137−
138− transform: Rotation {
139− readonly property int rotationAngle: applicationWindow.application &&
140− applicationWindow.application.rotatesWindowContents
141− ? ((360 - applicationWindow.surfaceOrientationAngle) % 360) : 0
142− origin.x: {
143− if (rotationAngle == 90) return applicationWindow.height / 2;
144− else if (rotationAngle == 270) return applicationWindow.width / 2;
145− else if (rotationAngle == 180) return applicationWindow.width / 2;
146− else return 0;
147− }
148− origin.y: {
149− if (rotationAngle == 90) return applicationWindow.height / 2;
150− else if (rotationAngle == 270) return applicationWindow.width / 2;
151− else if (rotationAngle == 180) return applicationWindow.height / 2;
152− else return 0;
153− }
154− angle: rotationAngle
155− }
156− }
157−}
qml/Stages/DesktopStage.qmldeleted−825
@@ -1,825 +0,0 @@
1−/*
2− * Copyright (C) 2014-2016 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−import QtQuick 2.4
18−import Ubuntu.Components 1.3
19−import Unity.Application 0.1
20−import "../Components/PanelState"
21−import "../Components"
22−import Utils 0.1
23−import Ubuntu.Gestures 0.1
24−import GlobalShortcut 1.0
25−
26−AbstractStage {
27− id: root
28− anchors.fill: parent
29− paintBackground: false
30−
31− // functions to be called from outside
32− function updateFocusedAppOrientation() { /* TODO */ }
33− function updateFocusedAppOrientationAnimated() { /* TODO */}
34− function pushRightEdge(amount) {
35− if (spread.state === "") {
36− edgeBarrier.push(amount);
37− }
38− }
39− function closeFocusedDelegate() {
40− if (priv.focusedAppDelegate && !priv.focusedAppDelegate.isDash) {
41− priv.focusedAppDelegate.close();
42− }
43− }
44−
45− // Used by TutorialRight
46− property bool spreadShown: spread.state == "altTab"
47−
48− // used by the snap windows (edge maximize) feature
49− readonly property alias previewRectangle: fakeRectangle
50−
51− mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null
52−
53− // application windows never rotate independently
54− mainAppWindowOrientationAngle: shellOrientationAngle
55−
56− orientationChangesEnabled: true
57−
58−
59− itemConfiningMouseCursor: !spreadShown && priv.focusedAppDelegate && priv.focusedAppDelegate.surface &&
60− priv.focusedAppDelegate.surface.confinesMousePointer ?
61− priv.focusedAppDelegate.clientAreaItem : null;
62−
63− GlobalShortcut {
64− id: showSpreadShortcut
65− shortcut: Qt.MetaModifier|Qt.Key_W
66− onTriggered: spread.state = "altTab"
67− }
68−
69− GlobalShortcut {
70− id: minimizeAllShortcut
71− shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_D
72− onTriggered: priv.minimizeAllWindows()
73− }
74−
75− GlobalShortcut {
76− id: maximizeWindowShortcut
77− shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
78− onTriggered: priv.focusedAppDelegate.maximize()
79− active: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.canBeMaximized
80− }
81−
82− GlobalShortcut {
83− id: maximizeWindowLeftShortcut
84− shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
85− onTriggered: priv.focusedAppDelegate.maximizeLeft()
86− active: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.canBeMaximizedLeftRight
87− }
88−
89− GlobalShortcut {
90− id: maximizeWindowRightShortcut
91− shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
92− onTriggered: priv.focusedAppDelegate.maximizeRight()
93− active: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.canBeMaximizedLeftRight
94− }
95−
96− GlobalShortcut {
97− id: minimizeRestoreShortcut
98− shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
99− onTriggered: priv.focusedAppDelegate.anyMaximized
100− ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
101− active: priv.focusedAppDelegate !== null
102− }
103−
104− GlobalShortcut {
105− shortcut: Qt.AltModifier|Qt.Key_Print
106− onTriggered: root.itemSnapshotRequested(priv.focusedAppDelegate)
107− active: priv.focusedAppDelegate !== null
108− }
109−
110− Connections {
111− target: root.topLevelSurfaceList
112− onCountChanged: {
113− if (spread.state == "altTab") {
114− spread.cancel();
115− }
116− }
117− }
118−
119− QtObject {
120− id: priv
121− objectName: "DesktopStagePrivate"
122−
123− property var focusedAppDelegate: null
124− onFocusedAppDelegateChanged: {
125− if (spread.state == "altTab") {
126− spread.state = "";
127− }
128− }
129−
130− property var foregroundMaximizedAppDelegate: null // for stuff like drop shadow and focusing maximized app by clicking panel
131−
132− function updateForegroundMaximizedApp() {
133− var found = false;
134− for (var i = 0; i < appRepeater.count && !found; i++) {
135− var item = appRepeater.itemAt(i);
136− if (item && item.visuallyMaximized) {
137− foregroundMaximizedAppDelegate = item;
138− found = true;
139− }
140− }
141− if (!found) {
142− foregroundMaximizedAppDelegate = null;
143− }
144− }
145−
146− function minimizeAllWindows() {
147− for (var i = 0; i < appRepeater.count; i++) {
148− var appDelegate = appRepeater.itemAt(i);
149− if (appDelegate && !appDelegate.minimized) {
150− appDelegate.minimize();
151− }
152− }
153− }
154−
155− function focusNext() {
156− for (var i = 0; i < appRepeater.count; i++) {
157− var appDelegate = appRepeater.itemAt(i);
158− if (appDelegate && !appDelegate.minimized) {
159− appDelegate.focus = true;
160− return;
161− }
162− }
163− }
164−
165− readonly property real virtualKeyboardHeight: SurfaceManager.inputMethodSurface
166− ? SurfaceManager.inputMethodSurface.inputBounds.height
167− : 0
168− }
169−
170− Connections {
171− target: PanelState
172− onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
173− onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.minimize(); } }
174− onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.restoreFromMaximized(); } }
175− }
176−
177− Binding {
178− target: PanelState
179− property: "buttonsVisible"
180− value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized // FIXME for Locally integrated menus
181− && spread.state == ""
182− }
183−
184− Binding {
185− target: PanelState
186− property: "title"
187− value: {
188− if (priv.focusedAppDelegate !== null && spread.state == "") {
189− if (priv.focusedAppDelegate.maximized)
190− return priv.focusedAppDelegate.title
191− else
192− return priv.focusedAppDelegate.appName
193− }
194− return ""
195− }
196− when: priv.focusedAppDelegate
197− }
198−
199− Binding {
200− target: PanelState
201− property: "dropShadow"
202− value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppDelegate !== null
203− }
204−
205− Binding {
206− target: PanelState
207− property: "closeButtonShown"
208− value: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized && !priv.focusedAppDelegate.isDash
209− }
210−
211− Component.onDestruction: {
212− PanelState.title = "";
213− PanelState.buttonsVisible = false;
214− PanelState.dropShadow = false;
215− }
216−
217− Instantiator {
218− model: root.applicationManager
219− delegate: Binding {
220− target: model.application
221− property: "requestedState"
222−
223− // TODO: figure out some lifecycle policy, like suspending minimized apps
224− // if running on a tablet or something.
225− // TODO: If the device has a dozen suspended apps because it was running
226− // in staged mode, when it switches to Windowed mode it will suddenly
227− // resume all those apps at once. We might want to avoid that.
228− value: ApplicationInfoInterface.RequestedRunning // Always running for now
229− }
230− }
231−
232− Binding {
233− target: MirFocusController
234− property: "focusedSurface"
235− value: priv.focusedAppDelegate ? priv.focusedAppDelegate.focusedSurface : null
236− when: !appRepeater.startingUp && root.parent
237− }
238−
239− FocusScope {
240− id: appContainer
241− objectName: "appContainer"
242− anchors.fill: parent
243− focus: spread.state !== "altTab"
244−
245− Wallpaper {
246− id: wallpaper
247− anchors.fill: parent
248− source: root.background
249− }
250−
251− TopLevelSurfaceRepeater {
252− id: appRepeater
253− model: topLevelSurfaceList
254− objectName: "appRepeater"
255−
256− delegate: FocusScope {
257− id: appDelegate
258− objectName: "appDelegate_" + model.id
259− // z might be overriden in some cases by effects, but we need z ordering
260− // to calculate occlusion detection
261− property int normalZ: topLevelSurfaceList.count - index
262− onNormalZChanged: {
263− if (visuallyMaximized) {
264− priv.updateForegroundMaximizedApp();
265− }
266− }
267− z: normalZ
268− x: requestedX // may be overridden in some states. Do not directly write to this.
269− y: requestedY // may be overridden in some states. Do not directly write to this.
270− property real requestedX: priv.focusedAppDelegate ? priv.focusedAppDelegate.x + units.gu(3) : (normalZ - 1) * units.gu(3)
271− property real requestedY: priv.focusedAppDelegate ? priv.focusedAppDelegate.y + units.gu(3) : normalZ * units.gu(3)
272−
273− Binding {
274− target: appDelegate
275− property: "y"
276− value: appDelegate.requestedY -
277− Math.min(appDelegate.requestedY - PanelState.panelHeight,
278− Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
279− when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
280− && SurfaceManager.inputMethodSurface
281− && SurfaceManager.inputMethodSurface.state != Mir.HiddenState
282− && SurfaceManager.inputMethodSurface.state != Mir.MinimizedState
283−
284− }
285−
286− width: decoratedWindow.width
287− height: decoratedWindow.height
288−
289− Connections {
290− target: root
291− onShellOrientationAngleChanged: {
292− // at this point decoratedWindow.surfaceOrientationAngle is the old shellOrientationAngle
293− if (application && application.rotatesWindowContents) {
294− if (state == "normal" || state == "restored") {
295− var angleDiff = decoratedWindow.surfaceOrientationAngle - shellOrientationAngle;
296− angleDiff = (360 + angleDiff) % 360;
297− if (angleDiff === 90 || angleDiff === 270) {
298− var aux = decoratedWindow.requestedHeight;
299− decoratedWindow.requestedHeight = decoratedWindow.requestedWidth + decoratedWindow.visibleDecorationHeight;
300− decoratedWindow.requestedWidth = aux - decoratedWindow.visibleDecorationHeight;
301− }
302− }
303− decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
304− } else {
305− decoratedWindow.surfaceOrientationAngle = 0;
306− }
307− }
308− }
309−
310− readonly property alias application: decoratedWindow.application
311− readonly property alias minimumWidth: decoratedWindow.minimumWidth
312− readonly property alias minimumHeight: decoratedWindow.minimumHeight
313− readonly property alias maximumWidth: decoratedWindow.maximumWidth
314− readonly property alias maximumHeight: decoratedWindow.maximumHeight
315− readonly property alias widthIncrement: decoratedWindow.widthIncrement
316− readonly property alias heightIncrement: decoratedWindow.heightIncrement
317− property int requestedWidth: -1
318− property int requestedHeight: -1
319−
320− readonly property bool maximized: windowState === WindowStateStorage.WindowStateMaximized
321− readonly property bool maximizedLeft: windowState === WindowStateStorage.WindowStateMaximizedLeft
322− readonly property bool maximizedRight: windowState === WindowStateStorage.WindowStateMaximizedRight
323− readonly property bool maximizedHorizontally: windowState === WindowStateStorage.WindowStateMaximizedHorizontally
324− readonly property bool maximizedVertically: windowState === WindowStateStorage.WindowStateMaximizedVertically
325− readonly property bool maximizedTopLeft: windowState === WindowStateStorage.WindowStateMaximizedTopLeft
326− readonly property bool maximizedTopRight: windowState === WindowStateStorage.WindowStateMaximizedTopRight
327− readonly property bool maximizedBottomLeft: windowState === WindowStateStorage.WindowStateMaximizedBottomLeft
328− readonly property bool maximizedBottomRight: windowState === WindowStateStorage.WindowStateMaximizedBottomRight
329− readonly property bool anyMaximized: maximized || maximizedLeft || maximizedRight || maximizedHorizontally || maximizedVertically ||
330− maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
331−
332− readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
333− readonly property alias fullscreen: decoratedWindow.fullscreen
334−
335− readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
336− readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
337− (maximumHeight == 0 || maximumHeight >= appContainer.height)
338− readonly property bool canBeCornerMaximized: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
339− (maximumHeight == 0 || maximumHeight >= appContainer.height/2)
340− readonly property bool canBeMaximizedHorizontally: maximumWidth == 0 || maximumWidth >= appContainer.width
341− readonly property bool canBeMaximizedVertically: maximumHeight == 0 || maximumHeight >= appContainer.height
342−
343− property int windowState: WindowStateStorage.WindowStateNormal
344− property bool animationsEnabled: true
345− property alias title: decoratedWindow.title
346− readonly property string appName: model.application ? model.application.name : ""
347− property bool visuallyMaximized: false
348− property bool visuallyMinimized: false
349−
350− readonly property var surface: model.surface
351− readonly property alias resizeArea: resizeArea
352− readonly property alias focusedSurface: decoratedWindow.focusedSurface
353− readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
354−
355− readonly property bool isDash: model.application.appId == "unity8-dash"
356− readonly property alias clientAreaItem: decoratedWindow.clientAreaItem
357−
358− function claimFocus() {
359− if (spread.state == "altTab") {
360− spread.cancel();
361− }
362− appDelegate.restore(true /* animated */, appDelegate.windowState);
363− }
364− Connections {
365− target: model.surface
366− onFocusRequested: claimFocus();
367− }
368− Connections {
369− target: model.application
370− onFocusRequested: {
371− if (!model.surface) {
372− // when an app has no surfaces, we assume there's only one entry representing it:
373− // this delegate.
374− claimFocus();
375− } else {
376− // if the application has surfaces, focus request should be at surface-level.
377− }
378− }
379− }
380−
381− onFocusChanged: {
382− if (appRepeater.startingUp)
383− return;
384−
385− if (focus) {
386− // If we're orphan (!parent) it means this stage is no longer the current one
387− // and will be deleted shortly. So we should no longer have a say over the model
388− if (root.parent) {
389− topLevelSurfaceList.raiseId(model.id);
390− }
391−
392− priv.focusedAppDelegate = appDelegate;
393− } else if (!focus && priv.focusedAppDelegate === appDelegate) {
394− priv.focusedAppDelegate = null;
395− // FIXME: No idea why the Binding{} doens't update when focusedAppDelegate turns null
396− MirFocusController.focusedSurface = null;
397− }
398− }
399− Component.onCompleted: {
400− if (application && application.rotatesWindowContents) {
401− decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
402− } else {
403− decoratedWindow.surfaceOrientationAngle = 0;
404− }
405−
406− // NB: We're differentiating if this delegate was created in response to a new entry in the model
407− // or if the Repeater is just populating itself with delegates to match the model it received.
408− if (!appRepeater.startingUp) {
409− // a top level window is always the focused one when it first appears, unfocusing
410− // any preexisting one
411− focus = true;
412− }
413− }
414− Component.onDestruction: {
415− if (!root.parent) {
416− // This stage is about to be destroyed. Don't mess up with the model at this point
417− return;
418− }
419−
420− if (visuallyMaximized) {
421− priv.updateForegroundMaximizedApp();
422− }
423−
424− if (focus) {
425− // focus some other window
426− for (var i = 0; i < appRepeater.count; i++) {
427− var appDelegate = appRepeater.itemAt(i);
428− if (appDelegate && !appDelegate.minimized && i != index) {
429− appDelegate.focus = true;
430− return;
431− }
432− }
433− }
434− }
435−
436− onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
437−
438− visible: (
439− !visuallyMinimized
440− && !greeter.fullyShown
441− && (priv.foregroundMaximizedAppDelegate === null || priv.foregroundMaximizedAppDelegate.normalZ <= z)
442− )
443− || decoratedWindow.fullscreen
444− || (spread.state == "altTab" && index === spread.highlightedIndex)
445−
446− function close() {
447− model.surface.close();
448− }
449−
450− function maximize(animated) {
451− animationsEnabled = (animated === undefined) || animated;
452− windowState = WindowStateStorage.WindowStateMaximized;
453− }
454− function maximizeLeft(animated) {
455− animationsEnabled = (animated === undefined) || animated;
456− windowState = WindowStateStorage.WindowStateMaximizedLeft;
457− }
458− function maximizeRight(animated) {
459− animationsEnabled = (animated === undefined) || animated;
460− windowState = WindowStateStorage.WindowStateMaximizedRight;
461− }
462− function maximizeHorizontally(animated) {
463− animationsEnabled = (animated === undefined) || animated;
464− windowState = WindowStateStorage.WindowStateMaximizedHorizontally;
465− }
466− function maximizeVertically(animated) {
467− animationsEnabled = (animated === undefined) || animated;
468− windowState = WindowStateStorage.WindowStateMaximizedVertically;
469− }
470− function maximizeTopLeft(animated) {
471− animationsEnabled = (animated === undefined) || animated;
472− windowState = WindowStateStorage.WindowStateMaximizedTopLeft;
473− }
474− function maximizeTopRight(animated) {
475− animationsEnabled = (animated === undefined) || animated;
476− windowState = WindowStateStorage.WindowStateMaximizedTopRight;
477− }
478− function maximizeBottomLeft(animated) {
479− animationsEnabled = (animated === undefined) || animated;
480− windowState = WindowStateStorage.WindowStateMaximizedBottomLeft;
481− }
482− function maximizeBottomRight(animated) {
483− animationsEnabled = (animated === undefined) || animated;
484− windowState = WindowStateStorage.WindowStateMaximizedBottomRight;
485− }
486− function minimize(animated) {
487− animationsEnabled = (animated === undefined) || animated;
488− windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
489− }
490− function restoreFromMaximized(animated) {
491− animationsEnabled = (animated === undefined) || animated;
492− windowState = WindowStateStorage.WindowStateRestored;
493− }
494− function restore(animated,state) {
495− animationsEnabled = (animated === undefined) || animated;
496− windowState = state || WindowStateStorage.WindowStateRestored;
497− windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
498− focus = true;
499− }
500−
501− function playFocusAnimation() {
502− focusAnimation.start()
503− }
504−
505− UbuntuNumberAnimation {
506− id: focusAnimation
507− target: appDelegate
508− property: "scale"
509− from: 0.98
510− to: 1
511− duration: UbuntuAnimation.SnapDuration
512− }
513−
514− // unlike requestedX/Y, this is the last known grab position before being pushed against edges/corners
515− // when restoring, the window should return to these, not to the place where it was dropped near the edge
516− property real restoredX
517− property real restoredY
518−
519− states: [
520− State {
521− name: "fullscreen"; when: appDelegate.fullscreen && !appDelegate.minimized
522− PropertyChanges {
523− target: appDelegate;
524− x: rotation == 0 ? 0 : (parent.width - width) / 2 + (shellOrientationAngle == 90 ? -PanelState.panelHeight : PanelState.panelHeight)
525− y: rotation == 0 ? -PanelState.panelHeight : (parent.height - height) / 2
526− requestedWidth: appContainer.width;
527− requestedHeight: appContainer.height;
528− }
529− },
530− State {
531− name: "normal";
532− when: appDelegate.windowState == WindowStateStorage.WindowStateNormal
533− PropertyChanges {
534− target: appDelegate;
535− visuallyMinimized: false;
536− visuallyMaximized: false
537− }
538− },
539− State {
540− name: "restored";
541− when: appDelegate.windowState == WindowStateStorage.WindowStateRestored
542− extend: "normal"
543− PropertyChanges {
544− target: appDelegate;
545− requestedX: restoredX;
546− requestedY: restoredY;
547− }
548− },
549− State {
550− name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
551− PropertyChanges {
552− target: appDelegate;
553− requestedX: root.leftMargin;
554− requestedY: 0;
555− requestedWidth: appContainer.width - root.leftMargin;
556− requestedHeight: appContainer.height;
557− visuallyMinimized: false;
558− visuallyMaximized: true
559− }
560− },
561− State {
562− name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
563− PropertyChanges {
564− target: appDelegate
565− requestedX: root.leftMargin
566− requestedY: PanelState.panelHeight
567− requestedWidth: (appContainer.width - root.leftMargin)/2
568− requestedHeight: appContainer.height - PanelState.panelHeight
569− }
570− },
571− State {
572− name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
573− extend: "maximizedLeft"
574− PropertyChanges {
575− target: appDelegate;
576− requestedX: (appContainer.width + root.leftMargin)/2
577− }
578− },
579− State {
580− name: "maximizedTopLeft"; when: appDelegate.maximizedTopLeft && !appDelegate.minimized
581− PropertyChanges {
582− target: appDelegate
583− requestedX: root.leftMargin
584− requestedY: PanelState.panelHeight
585− requestedWidth: (appContainer.width - root.leftMargin)/2
586− requestedHeight: (appContainer.height - PanelState.panelHeight)/2
587− }
588− },
589− State {
590− name: "maximizedTopRight"; when: appDelegate.maximizedTopRight && !appDelegate.minimized
591− extend: "maximizedTopLeft"
592− PropertyChanges {
593− target: appDelegate
594− requestedX: (appContainer.width + root.leftMargin)/2
595− }
596− },
597− State {
598− name: "maximizedBottomLeft"; when: appDelegate.maximizedBottomLeft && !appDelegate.minimized
599− PropertyChanges {
600− target: appDelegate
601− requestedX: root.leftMargin
602− requestedY: (appContainer.height + PanelState.panelHeight)/2
603− requestedWidth: (appContainer.width - root.leftMargin)/2
604− requestedHeight: appContainer.height/2
605− }
606− },
607− State {
608− name: "maximizedBottomRight"; when: appDelegate.maximizedBottomRight && !appDelegate.minimized
609− extend: "maximizedBottomLeft"
610− PropertyChanges {
611− target: appDelegate
612− requestedX: (appContainer.width + root.leftMargin)/2
613− }
614− },
615− State {
616− name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
617− PropertyChanges { target: appDelegate; requestedX: root.leftMargin; requestedY: requestedY; requestedWidth: appContainer.width - root.leftMargin }
618− },
619− State {
620− name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
621− PropertyChanges { target: appDelegate; requestedX: requestedX; requestedY: PanelState.panelHeight; requestedHeight: appContainer.height - PanelState.panelHeight }
622− },
623− State {
624− name: "minimized"; when: appDelegate.minimized
625− PropertyChanges {
626− target: appDelegate;
627− requestedX: -appDelegate.width / 2;
628− scale: units.gu(5) / appDelegate.width;
629− opacity: 0;
630− visuallyMinimized: true;
631− visuallyMaximized: false
632− }
633− }
634− ]
635− transitions: [
636− Transition {
637− to: "normal,restored"
638− enabled: appDelegate.animationsEnabled
639− PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
640− UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
641− },
642− Transition {
643− to: "minimized"
644− enabled: appDelegate.animationsEnabled
645− PropertyAction { target: appDelegate; property: "visuallyMaximized" }
646− SequentialAnimation {
647− UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
648− PropertyAction { target: appDelegate; property: "visuallyMinimized" }
649− ScriptAction {
650− script: {
651− if (appDelegate.minimized) {
652− appDelegate.focus = false;
653− priv.focusNext();
654− }
655− }
656− }
657− }
658− },
659− Transition {
660− to: "*" //maximized and fullscreen
661− enabled: appDelegate.animationsEnabled
662− PropertyAction { target: appDelegate; property: "visuallyMinimized" }
663− SequentialAnimation {
664− UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
665− PropertyAction { target: appDelegate; property: "visuallyMaximized" }
666− ScriptAction { script: { fakeRectangle.stop(); } }
667− }
668− }
669− ]
670−
671− Binding {
672− id: previewBinding
673− target: appDelegate
674− property: "z"
675− value: topLevelSurfaceList.count + 1
676− when: index == spread.highlightedIndex && spread.ready
677− }
678−
679− Binding {
680− target: PanelState
681− property: "buttonsAlwaysVisible"
682− value: appDelegate && appDelegate.maximized && touchControls.overlayShown
683− }
684−
685− WindowResizeArea {
686− id: resizeArea
687− objectName: "windowResizeArea"
688−
689− // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
690− anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
691−
692− target: appDelegate
693− minWidth: units.gu(10)
694− minHeight: units.gu(10)
695− borderThickness: units.gu(2)
696− windowId: model.application.appId // FIXME: Change this to point to windowId once we have such a thing
697− screenWidth: appContainer.width
698− screenHeight: appContainer.height
699− leftMargin: root.leftMargin
700−
701− onPressed: { appDelegate.focus = true; }
702−
703− Component.onCompleted: {
704− loadWindowState();
705− }
706−
707− property bool saveStateOnDestruction: true
708− Connections {
709− target: root
710− onStageAboutToBeUnloaded: {
711− resizeArea.saveWindowState();
712− resizeArea.saveStateOnDestruction = false;
713− fullscreenPolicy.active = false;
714− }
715− }
716− Component.onDestruction: {
717− if (saveStateOnDestruction) {
718− saveWindowState();
719− }
720− }
721− }
722−
723− DecoratedWindow {
724− id: decoratedWindow
725− objectName: "decoratedWindow"
726− anchors.left: appDelegate.left
727− anchors.top: appDelegate.top
728− application: model.application
729− surface: model.surface
730− active: appDelegate.focus
731− focus: true
732− maximizeButtonShown: appDelegate.canBeMaximized
733− overlayShown: touchControls.overlayShown
734− stageWidth: appContainer.width
735− stageHeight: appContainer.height
736−
737− requestedWidth: appDelegate.requestedWidth
738− requestedHeight: appDelegate.requestedHeight
739−
740− onCloseClicked: { appDelegate.close(); }
741− onMaximizeClicked: appDelegate.anyMaximized ? appDelegate.restoreFromMaximized() : appDelegate.maximize();
742− onMaximizeHorizontallyClicked: appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
743− onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
744− onMinimizeClicked: appDelegate.minimize()
745− onDecorationPressed: { appDelegate.focus = true; }
746− onDecorationReleased: fakeRectangle.commit();
747− }
748−
749− WindowControlsOverlay {
750− id: touchControls
751− target: appDelegate
752− stageWidth: appContainer.width
753− stageHeight: appContainer.height
754−
755− onFakeMaximizeAnimationRequested: if (!appDelegate.maximized) fakeRectangle.maximize(amount, true)
756− onFakeMaximizeLeftAnimationRequested: if (!appDelegate.maximizedLeft) fakeRectangle.maximizeLeft(amount, true)
757− onFakeMaximizeRightAnimationRequested: if (!appDelegate.maximizedRight) fakeRectangle.maximizeRight(amount, true)
758− onFakeMaximizeTopLeftAnimationRequested: if (!appDelegate.maximizedTopLeft) fakeRectangle.maximizeTopLeft(amount, true);
759− onFakeMaximizeTopRightAnimationRequested: if (!appDelegate.maximizedTopRight) fakeRectangle.maximizeTopRight(amount, true);
760− onFakeMaximizeBottomLeftAnimationRequested: if (!appDelegate.maximizedBottomLeft) fakeRectangle.maximizeBottomLeft(amount, true);
761− onFakeMaximizeBottomRightAnimationRequested: if (!appDelegate.maximizedBottomRight) fakeRectangle.maximizeBottomRight(amount, true);
762− onStopFakeAnimation: fakeRectangle.stop();
763− onDragReleased: fakeRectangle.commit();
764− }
765−
766− WindowedFullscreenPolicy {
767− id: fullscreenPolicy
768− active: true
769− surface: model.surface
770− }
771− }
772− }
773− }
774−
775− FakeMaximizeDelegate {
776− id: fakeRectangle
777− target: priv.focusedAppDelegate
778− leftMargin: root.leftMargin
779− appContainerWidth: appContainer.width
780− appContainerHeight: appContainer.height
781− }
782−
783− EdgeBarrier {
784− id: edgeBarrier
785−
786− // NB: it does its own positioning according to the specified edge
787− edge: Qt.RightEdge
788−
789− onPassed: { spread.show(); }
790− material: Component {
791− Item {
792− Rectangle {
793− width: parent.height
794− height: parent.width
795− rotation: 90
796− anchors.centerIn: parent
797− gradient: Gradient {
798− GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
799− GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
800− }
801− }
802− }
803− }
804− }
805−
806− SwipeArea {
807− direction: Direction.Leftwards
808− anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
809− width: units.gu(1)
810− onDraggingChanged: { if (dragging) { spread.show(); } }
811− }
812−
813− DesktopSpread {
814− id: spread
815− objectName: "spread"
816− anchors.fill: appContainer
817− workspace: appContainer
818− focus: state == "altTab"
819− altTabPressed: root.altTabPressed
820−
821− onPlayFocusAnimation: {
822− appRepeater.itemAt(index).playFocusAnimation();
823− }
824− }
825−}
qml/Tutorial/TutorialRight.qml+1−1
@@ -24,7 +24,7 @@ TutorialPage {
2424 property string usageScenario
2525
2626 // When on phone or tablet, fade out as the drag progresses
27− opacityOverride: usageScenario === "desktop" ? 1 : 1 - stage.dragProgress * 2
27+ opacityOverride: usageScenario === "desktop" ? 1 : 1 - stage.rightEdgeDragProgress * 2
2828
2929 // Else on desktop, fade out when the spread is shown
3030 Connections {
qml/Stages/graphics/dropshadow2gu@30.png → qml/graphics/dropshadow2gu@30.pngrenamed

Binary file not shown.

qml/Stages/graphics/dropshadow2gu@30.sci → qml/graphics/dropshadow2gu@30.scirenamed

The content is the same.

tests/graphics/applicationIcons/dash.png → tests/graphics/applicationIcons/dash@18.pngrenamed

Binary file not shown.

tests/mocks/Unity/Application/ApplicationInfo.cpp+2−2
@@ -79,7 +79,7 @@ void ApplicationInfo::createSurface()
7979{
8080 if (state() == ApplicationInfo::Stopped) { return; }
8181
82− QString surfaceName = name();
82+ QString surfaceName = name() + "s window title";
8383 if (m_surfaceList->count() > 0) {
8484 surfaceName.append(QString(" %1").arg(m_surfaceList->count()+1));
8585 }
@@ -138,7 +138,7 @@ void ApplicationInfo::createSurface()
138138
139139void ApplicationInfo::setIconId(const QString &iconId)
140140{
141− setIcon(QString("../../graphics/applicationIcons/%2@18.png")
141+ setIcon(QString(sourceDirectory() + "/tests/graphics/applicationIcons/%2@18.png")
142142 .arg(iconId));
143143}
144144
tests/mocks/Unity/Application/ApplicationManager.cpp+2
@@ -295,6 +295,7 @@ void ApplicationManager::buildListOfAvailableApplications()
295295 application->setAppId("unity8-dash");
296296 application->setName("Unity 8 Mock Dash");
297297 application->setScreenshotId("unity8-dash");
298+ application->setIconId("dash");
298299 m_availableApplications.append(application);
299300
300301 application = new ApplicationInfo(this);
@@ -403,6 +404,7 @@ void ApplicationManager::buildListOfAvailableApplications()
403404 application->setAppId("notes-app");
404405 application->setName("Notepad");
405406 application->setIconId("notepad");
407+// application->setStage(ApplicationInfoInterface::SideStage);
406408 m_availableApplications.append(application);
407409
408410 application = new ApplicationInfo(this);
tests/mocks/Unity/Application/MirSurface.cpp+1
@@ -256,6 +256,7 @@ void MirSurface::updateVisibility()
256256
257257 m_visible = newVisible;
258258 Q_EMIT visibleChanged(m_visible);
259+ updateInputBoundsAfterResize();
259260 }
260261}
261262
tests/mocks/Unity/Application/MirSurfaceItem.cpp+13
@@ -60,9 +60,13 @@ MirSurfaceItem::MirSurfaceItem(QQuickItem *parent)
6060
6161 connect(this, &QQuickItem::activeFocusChanged, this, &MirSurfaceItem::updateMirSurfaceActiveFocus);
6262 connect(this, &QQuickItem::visibleChanged, this, &MirSurfaceItem::updateMirSurfaceVisibility);
63+
6364 connect(this, &MirSurfaceItem::consumesInputChanged, this, [this]() {
6465 updateMirSurfaceActiveFocus(hasActiveFocus());
6566 });
67+
68+ // We're just clipping contents in the mock. The real QtMir would copy only relevant buffer instead
69+ setClip(true);
6670}
6771
6872MirSurfaceItem::~MirSurfaceItem()
@@ -181,6 +185,9 @@ void MirSurfaceItem::createQmlContentItem()
181185 m_qmlItem = qobject_cast<QQuickItem*>(m_qmlContentComponent->create());
182186 m_qmlItem->setParentItem(this);
183187
188+ m_qmlItem->setWidth(m_surfaceWidth);
189+ m_qmlItem->setHeight(m_surfaceHeight);
190+
184191 setImplicitWidth(m_qmlItem->implicitWidth());
185192 setImplicitHeight(m_qmlItem->implicitHeight());
186193
@@ -350,6 +357,7 @@ int MirSurfaceItem::surfaceWidth() const
350357
351358void MirSurfaceItem::setSurfaceWidth(int value)
352359{
360+// qDebug() << "setSurfaceWidth called" << value;
353361 if (value != -1 && m_surfaceWidth != value) {
354362 m_surfaceWidth = value;
355363 Q_EMIT surfaceWidthChanged(m_surfaceWidth);
@@ -375,6 +383,11 @@ void MirSurfaceItem::updateSurfaceSize()
375383{
376384 if (m_qmlSurface && m_surfaceWidth > 0 && m_surfaceHeight > 0) {
377385 m_qmlSurface->resize(m_surfaceWidth, m_surfaceHeight);
386+ if (m_qmlItem) {
387+ m_qmlItem->setWidth(m_surfaceWidth);
388+ m_qmlItem->setHeight(m_surfaceHeight);
389+ }
390+ setImplicitSize(m_surfaceWidth, m_surfaceHeight);
378391 }
379392}
380393
tests/mocks/Unity/Application/MirSurfaceListModel.cpp−1
@@ -84,7 +84,6 @@ void MirSurfaceListModel::prependSurface(MirSurface *surface)
8484
8585void MirSurfaceListModel::connectSurface(MirSurface *surface)
8686{
87− connect(surface, &MirSurface::raiseRequested, this, [this, surface](){ this->raise(surface); });
8887 connect(surface, &QObject::destroyed, this, [this, surface](){ this->removeSurface(surface); });
8988}
9089
tests/mocks/Unity/Application/VirtualKeyboard.cpp+9−7
@@ -39,13 +39,15 @@ void VirtualKeyboard::updateInputBoundsAfterResize()
3939{
4040 int width = this->width();
4141
42− int height;
43− if (this->width() > this->height()) {
44− // landscape
45− height = this->height() * 0.4;
46− } else {
47− // portrait
48− height = this->width() * 0.6;
42+ int height = 0;
43+ if (visible()) {
44+ if (this->width() > this->height()) {
45+ // landscape
46+ height = this->height() * 0.4;
47+ } else {
48+ // portrait
49+ height = this->width() * 0.6;
50+ }
4951 }
5052
5153 int x = 0;
tests/mocks/Unity/Application/resources/MirSurfaceItem.qml+2−5
@@ -20,11 +20,8 @@ Rectangle {
2020 id: root
2121 color: "pink"
2222
23− implicitWidth: units.gu(40)
24− implicitHeight: units.gu(70)
25−
26− width: parent ? parent.width : implicitWidth
27− height: parent ? parent.height : implicitHeight
23+ implicitWidth: width
24+ implicitHeight: height
2825
2926 property alias screenshotSource: screenshotImage.source
3027 property int orientationAngle
tests/qmltests/CMakeLists.txt+8−8
@@ -86,14 +86,14 @@ add_unity8_qmltest(Panel Panel)
8686add_unity8_qmltest(Panel/Indicators IndicatorsLight)
8787add_unity8_qmltest(Panel/Indicators MenuItemFactory)
8888add_unity8_qmltest(Panel/Indicators MessageMenuItemFactory)
89−add_unity8_qmltest(Stages ApplicationWindow)
90−add_unity8_qmltest(Stages DesktopStage)
91−add_unity8_qmltest(Stages PhoneStage)
92−add_unity8_qmltest(Stages SpreadDelegate)
93−add_unity8_qmltest(Stages SurfaceContainer)
94−add_unity8_qmltest(Stages TabletStage)
95−add_unity8_qmltest(Stages WindowResizeArea)
96−add_unity8_qmltest(Stages Splash)
89+add_unity8_qmltest(Stage ApplicationWindow)
90+add_unity8_qmltest(Stage DesktopStage)
91+add_unity8_qmltest(Stage PhoneStage)
92+add_unity8_qmltest(Stage SurfaceContainer)
93+add_unity8_qmltest(Stage TabletStage)
94+add_unity8_qmltest(Stage WindowResizeArea)
95+add_unity8_qmltest(Stage Splash)
96+add_unity8_qmltest(Stage DecoratedWindow)
9797add_unity8_qmltest(Tutorial Tutorial)
9898add_unity8_qmltest(Wizard Wizard ENVIRONMENT "OXIDE_NO_SANDBOX=1")
9999add_unity8_qmltest(utils/Unity/Test UnityTest)
tests/qmltests/Stages/ApplicationCheckBox.qml → tests/qmltests/Stage/ApplicationCheckBox.qmlrenamed

The content is the same.

tests/qmltests/Stages/SizeHintField.qml → tests/qmltests/Stage/SizeHintField.qmlrenamed

The content is the same.

tests/qmltests/Stages/SurfaceManagerControls.qml → tests/qmltests/Stage/SurfaceManagerControls.qmlrenamed

The content is the same.

tests/qmltests/Stages/SurfaceManagerField.qml → tests/qmltests/Stage/SurfaceManagerField.qmlrenamed

The content is the same.

tests/qmltests/Stages/tst_ApplicationWindow.qml → tests/qmltests/Stage/tst_ApplicationWindow.qmlrenamed+1−1
@@ -19,7 +19,7 @@ import QtQuick.Layouts 1.1
1919import QtTest 1.0
2020import Unity.Test 0.1 as UT
2121import ".."
22−import "../../../qml/Stages"
22+import "../../../qml/Stage"
2323import Ubuntu.Components 1.3
2424import Ubuntu.Components.ListItems 1.3 as ListItem
2525import Unity.Application 0.1
tests/qmltests/Stage/tst_DecoratedWindow.qmlnew file+232
@@ -0,0 +1,232 @@
1+/*
2+ * Copyright 2014-2015 Canonical Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+import QtQuick 2.4
18+import QtTest 1.0
19+import Unity.Test 0.1 as UT
20+import ".."
21+import "../../../qml/Components"
22+import "../../../qml/Stage"
23+import Ubuntu.Components 1.3
24+import Ubuntu.Components.ListItems 1.3 as ListItem
25+import Unity.Application 0.1
26+
27+Rectangle {
28+ color: "red"
29+ id: root
30+ width: fakeShell.shortestDimension + controls.width
31+ height: fakeShell.longestDimension
32+
33+ property QtObject fakeApplication: null
34+
35+ Item {
36+ id: fakeShell
37+
38+ readonly property real shortestDimension: units.gu(100)
39+ readonly property real longestDimension: units.gu(70)
40+
41+ width: landscape ? longestDimension : shortestDimension
42+ height: landscape ? shortestDimension : longestDimension
43+
44+ x: landscape ? (height - width) / 2 : 0
45+ y: landscape ? (width - height) / 2 : 0
46+
47+ property bool landscape: orientationAngle == 90 || orientationAngle == 270
48+ property int orientationAngle: shellOrientationAngleSelector.value
49+
50+ rotation: orientationAngle
51+
52+ Loader {
53+ id: loader
54+ property int windowedX: units.gu(1)
55+ property int windowedY: units.gu(1)
56+ property int windowedWidth: units.gu(50)
57+ property int windowedHeight: units.gu(40)
58+ x: windowedX
59+ y: windowedY
60+ width: item ? item.implicitWidth : 0
61+ height: item ? item.implicitHeight : 0
62+
63+ active: false
64+ property bool itemDestroyed: false
65+
66+ sourceComponent: DecoratedWindow {
67+ anchors.fill: parent
68+ application: fakeApplication
69+ requestedWidth: loader.windowedWidth
70+ requestedHeight: loader.windowedHeight
71+ surface: fakeApplication && fakeApplication.surfaceList.count > 0 ? fakeApplication.surfaceList.get(0) : null
72+
73+ Component.onDestruction: {
74+ loader.itemDestroyed = true;
75+ }
76+ Component.onCompleted: {
77+ loader.itemDestroyed = false;
78+ }
79+ }
80+
81+ Rectangle { anchors.fill: parent; color: "green"; opacity: 1 }
82+ }
83+ WindowResizeArea {
84+ target: loader
85+ borderThickness: units.gu(2)
86+ }
87+ }
88+
89+
90+ Rectangle {
91+ id: controls
92+ color: "white"
93+ width: units.gu(30)
94+ anchors {
95+ top: parent.top
96+ bottom: parent.bottom
97+ right: parent.right
98+ }
99+
100+ Column {
101+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
102+ spacing: units.gu(1)
103+ Button {
104+ id: loadWithWeatherApp
105+ text: "Load with ubuntu-weather-app"
106+ onClicked: { testCase.restartWithApp("ubuntu-weather-app"); }
107+ }
108+ Button {
109+ id: loadWithGalleryApp
110+ text: "Load with gallery-app"
111+ onClicked: { testCase.restartWithApp("gallery-app"); }
112+ }
113+ Button {
114+ text: "toggle has decoration"
115+ onClicked: loader.item.hasDecoration = !loader.item.hasDecoration
116+ }
117+ Button {
118+ text: "show/hide decoration"
119+ onClicked: loader.item.showDecoration = !loader.item.showDecoration
120+ }
121+ Label {
122+ text: "scale to preview progress"
123+ }
124+
125+ Slider {
126+ value: 0
127+ minimumValue: 0
128+ maximumValue: 1
129+ live: true
130+ onValueChanged: loader.item.scaleToPreviewProgress = value
131+ }
132+ ListItem.ItemSelector {
133+ id: shellOrientationAngleSelector
134+ anchors { left: parent.left; right: parent.right }
135+ text: "shellOrientationAngle"
136+ model: ["0", "90", "180", "270"]
137+ property int value: selectedIndex * 90
138+ }
139+ Button {
140+ text: "matchShellOrientation()"
141+ onClicked: { loader.item.matchShellOrientation(); }
142+ }
143+ Button {
144+ text: "animateToShellOrientation()"
145+ onClicked: { loader.item.animateToShellOrientation(); }
146+ }
147+ }
148+ }
149+
150+ UT.UnityTestCase {
151+ id: testCase
152+ name: "DecoratedWindow"
153+ when: windowShown
154+
155+ property Item decoratedWindow: loader.item
156+
157+ function init() {
158+ }
159+
160+ function cleanup() {
161+ unloadWindow();
162+ root.fakeApplication = null;
163+ killApps();
164+ }
165+
166+ function restartWithApp(appId) {
167+ if (loader.active) {
168+ unloadWindow();
169+ }
170+ if (root.fakeApplication) {
171+ ApplicationManager.stopApplication(root.fakeApplication.appId);
172+ }
173+
174+ root.fakeApplication = ApplicationManager.startApplication(appId);
175+ loader.active = true;
176+ tryCompare(loader, "status", Loader.Ready);
177+ }
178+
179+ function unloadWindow() {
180+ loader.active = false;
181+ tryCompare(loader, "status", Loader.Null);
182+ tryCompare(loader, "item", null);
183+ // Loader.status might be Loader.Null and Loader.item might be null but the Loader
184+ // item might still be alive. So if we set Loader.active back to true
185+ // again right now we will get the very same Shell instance back. So no reload
186+ // actually took place. Likely because Loader waits until the next event loop
187+ // iteration to do its work. So to ensure the reload, we will wait until the
188+ // Shell instance gets destroyed.
189+ tryCompare(loader, "itemDestroyed", true);
190+
191+ }
192+
193+ function test_keepsSceneTransformationWhenShellRotates_data() {
194+ return [
195+ {tag: "0", selectedIndex: 0},
196+ {tag: "90", selectedIndex: 1},
197+ {tag: "180", selectedIndex: 2},
198+ {tag: "270", selectedIndex: 3}
199+ ];
200+ }
201+ function test_keepsSceneTransformationWhenShellRotates(data) {
202+ loadWithGalleryApp.clicked();
203+
204+ // Wait until it reaches the state we are interested on.
205+ // It begins with "startingUp"
206+ tryCompare(decoratedWindow, "counterRotate", false);
207+ var oldWidth = decoratedWindow.width
208+ var oldHeight = decoratedWindow.height
209+ var oldX = decoratedWindow.x
210+ var oldY = decoratedWindow.y
211+
212+ shellOrientationAngleSelector.selectedIndex = data.selectedIndex;
213+
214+ // must keep same aspect ratio
215+ compare(decoratedWindow.width, oldWidth);
216+ compare(decoratedWindow.height, oldHeight);
217+
218+
219+ // and scene transformation must be the identity (ie, no rotation or translation)
220+ compare(decoratedWindow.x, oldX);
221+ compare(decoratedWindow.y, oldY);
222+ }
223+
224+ function test_showHighLight() {
225+ loadWithGalleryApp.clicked();
226+ var highlightRect = findChild(loader.item, "selectionHighlight")
227+ tryCompare(highlightRect, "visible", false)
228+ loader.item.showHighlight = true;
229+ tryCompare(highlightRect, "visible", true)
230+ }
231+ }
232+}
tests/qmltests/Stages/tst_DesktopStage.qml → tests/qmltests/Stage/tst_DesktopStage.qmlrenamed+29−71
@@ -24,14 +24,14 @@ import WindowManager 0.1
2424import Utils 0.1
2525
2626import ".." // For EdgeBarrierControls
27−import "../../../qml/Stages"
27+import "../../../qml/Stage"
2828import "../../../qml/Components"
2929import "../../../qml/Components/PanelState"
3030
3131Item {
3232 id: root
33− width: desktopStageLoader.width + controls.width
34− height: desktopStageLoader.height
33+ width: stageLoader.width + controls.width
34+ height: stageLoader.height
3535
3636 property var greeter: { fullyShown: true }
3737
@@ -63,7 +63,7 @@ Item {
6363 }
6464
6565 Loader {
66− id: desktopStageLoader
66+ id: stageLoader
6767 x: ((root.width - controls.width) - width) / 2
6868 y: (root.height - height) / 2
6969 width: units.gu(160*0.9)
@@ -73,7 +73,7 @@ Item {
7373
7474 property bool itemDestroyed: false
7575 sourceComponent: Component {
76− DesktopStage {
76+ Stage {
7777 anchors.fill: parent
7878 background: "/usr/share/backgrounds/warty-final-ubuntu.png"
7979 focus: true
@@ -82,17 +82,19 @@ Item {
8282 edgeBarrierControls.target = testCase.findChild(this, "edgeBarrierController");
8383 }
8484 Component.onDestruction: {
85− desktopStageLoader.itemDestroyed = true;
85+ stageLoader.itemDestroyed = true;
8686 }
8787 orientations: Orientations {}
8888 applicationManager: ApplicationManager
8989 topLevelSurfaceList: topSurfaceList
90+ interactive: true
91+ mode: "windowed"
9092 }
9193 }
9294
9395 MouseArea {
9496 id: clickThroughTester
95− anchors.fill: desktopStageLoader.item
97+ anchors.fill: stageLoader.item
9698 acceptedButtons: Qt.AllButtons
9799 hoverEnabled: true
98100 }
@@ -130,7 +132,7 @@ Item {
130132 id: edgeBarrierControls
131133 text: "Drag here to pull out spread"
132134 backgroundColor: "blue"
133− onDragged: { desktopStageLoader.item.pushRightEdge(amount); }
135+ onDragged: { stageLoader.item.pushRightEdge(amount); }
134136 }
135137
136138 Divider {}
@@ -157,7 +159,7 @@ Item {
157159 name: "DesktopStage"
158160 when: windowShown
159161
160− property Item desktopStage: desktopStageLoader.status === Loader.Ready ? desktopStageLoader.item : null
162+ property Item stage: stageLoader.status === Loader.Ready ? stageLoader.item : null
161163
162164 function init() {
163165 // wait until unity8-dash is up and running.
@@ -173,29 +175,29 @@ Item {
173175 }
174176
175177 function cleanup() {
176− desktopStageLoader.itemDestroyed = false;
177− desktopStageLoader.active = false;
178+ stageLoader.itemDestroyed = false;
179+ stageLoader.active = false;
178180
179− tryCompare(desktopStageLoader, "status", Loader.Null);
180− tryCompare(desktopStageLoader, "item", null);
181+ tryCompare(stageLoader, "status", Loader.Null);
182+ tryCompare(stageLoader, "item", null);
181183 // Loader.status might be Loader.Null and Loader.item might be null but the Loader
182184 // actually took place. Likely because Loader waits until the next event loop
183185 // iteration to do its work. So to ensure the reload, we will wait until the
184186 // Shell instance gets destroyed.
185− tryCompare(desktopStageLoader, "itemDestroyed", true);
187+ tryCompare(stageLoader, "itemDestroyed", true);
186188
187189 killApps();
188190
189191 root.resetGeometry();
190192
191− desktopStageLoader.active = true;
192− tryCompare(desktopStageLoader, "status", Loader.Ready);
193+ stageLoader.active = true;
194+ tryCompare(stageLoader, "status", Loader.Ready);
193195
194196 mouseEaterSpy.clear();
195197 }
196198
197199 function waitUntilAppSurfaceShowsUp(surfaceId) {
198− var appDelegate = findChild(desktopStage, "appDelegate_" + surfaceId);
200+ var appDelegate = findChild(stage, "appDelegate_" + surfaceId);
199201 verify(appDelegate);
200202 var appWindow = findChild(appDelegate, "appWindow");
201203 verify(appWindow);
@@ -213,7 +215,7 @@ Item {
213215 if (app) {
214216 for (var i = 0; i < topSurfaceList.count; i++) {
215217 if (topSurfaceList.applicationAt(i).appId === appId) {
216− var appRepeater = findChild(desktopStage, "appRepeater");
218+ var appRepeater = findChild(stage, "appRepeater");
217219 verify(appRepeater);
218220 return appRepeater.itemAt(i);
219221 }
@@ -226,7 +228,7 @@ Item {
226228 waitUntilAppSurfaceShowsUp(surfaceId);
227229 compare(app.surfaceList.count, 1);
228230
229− return findChild(desktopStage, "appDelegate_" + surfaceId);
231+ return findChild(stage, "appDelegate_" + surfaceId);
230232 } catch(err) {
231233 throw new Error("startApplication("+appId+") called from line " + util.callerLine(1) + " failed!");
232234 }
@@ -324,7 +326,7 @@ Item {
324326 }
325327
326328 function findDecoratedWindow(surfaceId) {
327− var appDelegate = findChild(desktopStage, "appDelegate_" + surfaceId);
329+ var appDelegate = findChild(stage, "appDelegate_" + surfaceId);
328330 if (!appDelegate) {
329331 console.warn("Could not find appDelegate for surfaceId="+surfaceId);
330332 return null;
@@ -333,7 +335,7 @@ Item {
333335 }
334336
335337 function findWindowDecoration(surfaceId) {
336− var appDelegate = findChild(desktopStage, "appDelegate_" + surfaceId);
338+ var appDelegate = findChild(stage, "appDelegate_" + surfaceId);
337339 if (!appDelegate) {
338340 console.warn("Could not find appDelegate for surfaceId="+surfaceId);
339341 return null;
@@ -465,8 +467,8 @@ Item {
465467
466468 mouseClick(dialerDelegate);
467469
468− var desktopStagePriv = findInvisibleChild(desktopStage, "DesktopStagePrivate");
469− tryCompare(desktopStagePriv, "focusedAppDelegate", dialerDelegate);
470+ var stagePriv = findInvisibleChild(stage, "DesktopStagePrivate");
471+ tryCompare(stagePriv, "focusedAppDelegate", dialerDelegate);
470472
471473 keyClick(Qt.Key_F4, Qt.AltModifier); // Alt+F4 shortcut to close
472474
@@ -517,35 +519,6 @@ Item {
517519 tryCompare(MirFocusController, "focusedSurface", null); // verify still no surface is focused
518520 }
519521
520− function test_oskDisplacesWindow_data() {
521− return [
522− {tag: "no need to displace", windowHeight: units.gu(10), windowY: units.gu(5), targetDisplacement: units.gu(5), oskEnabled: true},
523− {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight, oskEnabled: true},
524− {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight, oskEnabled: true},
525− {tag: "osk not on this screen", windowHeight: units.gu(40), windowY: units.gu(10), targetDisplacement: units.gu(10), oskEnabled: false},
526− ]
527− }
528−
529− function test_oskDisplacesWindow(data) {
530− desktopStageLoader.item.oskEnabled = data.oskEnabled
531− var dashAppDelegate = startApplication("unity8-dash");
532− var oldOSKState = SurfaceManager.inputMethodSurface.state;
533− SurfaceManager.inputMethodSurface.state = Mir.RestoredState;
534− verify(dashAppDelegate);
535− dashAppDelegate.requestedHeight = data.windowHeight;
536− dashAppDelegate.requestedY = data.windowY;
537− SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
538− var initialY = dashAppDelegate.y;
539− verify(initialY > PanelState.panelHeight);
540−
541− SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, root.height / 2, root.width, root.height / 2));
542− tryCompare(dashAppDelegate, "y", data.targetDisplacement);
543−
544− SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
545− tryCompare(dashAppDelegate, "y", initialY);
546− SurfaceManager.inputMethodSurface.state = oldOSKState;
547− }
548−
549522 function test_minimizeApplicationHidesSurface() {
550523 compare(topSurfaceList.applicationAt(0).appId, "unity8-dash");
551524 var dashSurface = topSurfaceList.surfaceAt(0);
@@ -598,6 +571,8 @@ Item {
598571 var gmailDelegate = startApplication("gmail-webapp");
599572 verify(gmailDelegate);
600573
574+ wait(2000)
575+
601576 var gmailMaximizeButton = findChild(gmailDelegate, "maximizeWindowButton");
602577 verify(gmailMaximizeButton);
603578 mouseClick(gmailMaximizeButton);
@@ -610,6 +585,7 @@ Item {
610585 tryCompare(mapApp.surfaceList.get(0), "visible", false);
611586
612587 ApplicationManager.stopApplication("gmail-webapp");
588+ wait(2000)
613589
614590 tryCompare(mapApp.surfaceList.get(0), "visible", true);
615591 tryCompare(dialerApp.surfaceList.get(0), "visible", true);
@@ -709,7 +685,7 @@ Item {
709685 tryCompare(overlay, "visible", data.result);
710686
711687 if (data.result) { // if shown, try to hide it by clicking outside
712− mouseClick(desktopStage);
688+ mouseClick(stage);
713689 tryCompare(overlay, "visible", false);
714690 }
715691 }
@@ -768,24 +744,6 @@ Item {
768744 tryCompareFunction(function(){return posBefore == posAfter;}, data.button !== Qt.LeftButton ? true : false);
769745 }
770746
771− function test_preventMouseEventsThruDesktopSpread() {
772− var spread = findChild(desktopStage, "spread");
773− verify(spread);
774− spread.show();
775− tryCompareFunction( function(){ return spread.ready }, true );
776−
777− mouseEaterSpy.signalName = "wheel";
778− mouseWheel(spread, spread.width/2, spread.height/2, 10, 10);
779− tryCompare(mouseEaterSpy, "count", 0);
780−
781− mouseEaterSpy.clear();
782− mouseEaterSpy.signalName = "clicked";
783− mouseClick(spread, spread.width/2, spread.height/2, Qt.RightButton);
784− tryCompare(mouseEaterSpy, "count", 0);
785−
786− spread.cancel();
787− }
788−
789747 function test_eatWindowDecorationMouseEvents_data() {
790748 return [
791749 {tag: "left mouse click", signalName: "clicked", button: Qt.LeftButton },
tests/qmltests/Stages/tst_PhoneStage.qml → tests/qmltests/Stage/tst_PhoneStage.qmlrenamed+149−211
@@ -19,7 +19,7 @@ import QtTest 1.0
1919import Unity.Test 0.1 as UT
2020import ".."
2121import "../../../qml/Components"
22−import "../../../qml/Stages"
22+import "../../../qml/Stage"
2323import Ubuntu.Components 1.3
2424import Unity.Application 0.1
2525import WindowManager 0.1
@@ -31,16 +31,16 @@ Item {
3131
3232 property var greeter: { fullyShown: true }
3333
34− PhoneStage {
35− id: phoneStage
34+ Stage {
35+ id: stage
3636 anchors { fill: parent; rightMargin: units.gu(30) }
3737 focus: true
3838 dragAreaWidth: units.gu(2)
39− maximizedAppTopMargin: units.gu(3)
4039 interactive: true
4140 shellOrientation: Qt.PortraitOrientation
4241 orientations: Orientations {}
4342 applicationManager: ApplicationManager
43+ mode: "staged"
4444 topLevelSurfaceList: TopLevelSurfaceList {
4545 id: topLevelSurfaceList
4646 applicationsModel: ApplicationManager
@@ -66,9 +66,9 @@ Item {
6666 id: edgeBarrierControls
6767 text: "Drag here to pull out spread"
6868 backgroundColor: "blue"
69− onDragged: { phoneStage.pushRightEdge(amount); }
69+ onDragged: { stage.pushRightEdge(amount); }
7070 Component.onCompleted: {
71− edgeBarrierControls.target = testCase.findChild(phoneStage, "edgeBarrierController");
71+ edgeBarrierControls.target = testCase.findChild(stage, "edgeBarrierController");
7272 }
7373 }
7474 Repeater {
@@ -84,11 +84,37 @@ Item {
8484 name: "PhoneStage"
8585 when: windowShown
8686
87+ function init() {
88+ // wait until unity8-dash is up and running.
89+ // it's started automatically by ApplicationManager mock implementation
90+ tryCompare(ApplicationManager, "count", 1);
91+ var dashApp = ApplicationManager.findApplication("unity8-dash");
92+ verify(dashApp);
93+ tryCompare(dashApp, "state", ApplicationInfoInterface.Running);
94+ }
95+
96+ function cleanup() {
97+ ApplicationManager.requestFocusApplication("unity8-dash");
98+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
99+ tryCompare(stage, "state", "staged");
100+ waitForRendering(stage);
101+
102+ killApps();
103+ // wait for Stage to stabilize back into its initial state
104+ var appRepeater = findChild(stage, "appRepeater");
105+ tryCompare(appRepeater, "count", 1);
106+ tryCompare(appRepeater.itemAt(0), "x", 0);
107+
108+ stage.shellOrientationAngle = 0;
109+
110+ waitForRendering(stage)
111+ }
112+
87113 function findAppWindowForSurfaceId(surfaceId) {
88− var delegateObjectName = "spreadDelegate_" + surfaceId;
89− var spreadDelegate = findChild(phoneStage, delegateObjectName);
114+ var delegateObjectName = "appDelegate_" + surfaceId;
115+ var spreadDelegate = findChild(stage, delegateObjectName);
90116 if (!spreadDelegate) {
91− console.warn("Failed to find " + delegateObjectName + " in phoneStage ("+phoneStage+")");
117+ console.warn("Failed to find " + delegateObjectName + " in stage");
92118 return null;
93119 }
94120 var appWindow = findChild(spreadDelegate, "appWindow");
@@ -108,33 +134,34 @@ Item {
108134 function addApps(count) {
109135 if (count == undefined) count = 1;
110136 for (var i = 0; i < count; i++) {
137+ var startingAppId = ApplicationManager.availableApplications[ApplicationManager.count];
111138 var appSurfaceId = topLevelSurfaceList.nextId;
112− var app = ApplicationManager.startApplication(ApplicationManager.availableApplications[ApplicationManager.count])
139+ var app = ApplicationManager.startApplication(startingAppId)
113140 tryCompare(app, "state", ApplicationInfoInterface.Running)
114− var spreadView = findChild(phoneStage, "spreadView");
115− tryCompare(spreadView, "contentX", -spreadView.shift);
116141 waitUntilAppSurfaceShowsUp(appSurfaceId);
117− waitForRendering(phoneStage)
142+ waitForRendering(stage)
143+ tryCompare(ApplicationManager, "focusedApplicationId", startingAppId)
118144 }
119145 }
120146
121147 function performEdgeSwipeToShowAppSpread() {
122− var spreadView = findChild(phoneStage, "spreadView");
123−
124− // Keep it inside the PhoneStage otherwise the controls on the right side will
148+ // Keep it inside the Stage otherwise the controls on the right side will
125149 // capture the press thus the "- 2" on startX.
126− var startX = phoneStage.width - 2;
127− var startY = phoneStage.height / 2;
150+ var startX = stage.width - 2;
151+ var startY = stage.height / 2;
128152 var endY = startY;
129− var endX = phoneStage.width / 2;
153+ var endX = stage.width / 2;
130154
131− touchFlick(phoneStage, startX, startY, endX, endY,
155+ touchFlick(stage, startX, startY, endX, endY,
132156 true /* beginTouch */, true /* endTouch */, units.gu(10), 50);
133157
134− tryCompare(spreadView, "phase", 2);
135− tryCompare(spreadView, "flicking", false);
136− tryCompare(spreadView, "moving", false);
137− waitForRendering(phoneStage);
158+ tryCompare(stage, "state", "spread");
159+ // Make sure all the transitions have finished
160+ var appRepeater = findChild(stage, "appRepeater");
161+ for (var i = 0; i < appRepeater.count; i++) {
162+ waitUntilTransitionsEnd(appRepeater.itemAt(i));
163+ }
164+ waitForRendering(stage);
138165 }
139166
140167 function swipeSurfaceUpwards(surfaceId) {
@@ -163,7 +190,7 @@ Item {
163190
164191 function waitUntilAppDelegateStopsMoving(targetSurfaceId)
165192 {
166− var targetAppDelegate = findChild(phoneStage, "spreadDelegate_" + targetSurfaceId);
193+ var targetAppDelegate = findChild(stage, "appDelegate_" + targetSurfaceId);
167194 verify(targetAppDelegate);
168195 var lastValue = undefined;
169196 do {
@@ -172,79 +199,39 @@ Item {
172199 } while (lastValue != targetAppDelegate.animatedProgress);
173200 }
174201
175− function test_shortFlick() {
176− addApps(2)
177− var startX = phoneStage.width - units.gu(1);
178− var startY = phoneStage.height / 2;
179− var endX = startX - units.gu(4);
180− var endY = startY;
181−
182− var activeApp = ApplicationManager.get(0);
183− var inactiveApp = ApplicationManager.get(1);
184−
185− touchFlick(phoneStage, startX, startY, endX, endY,
186− true /* beginTouch */, true /* endTouch */, units.gu(10), 50);
187−
188− tryCompare(ApplicationManager, "focusedApplicationId", inactiveApp.appId)
189−
190− touchFlick(phoneStage, startX, startY, endX, endY,
191− true /* beginTouch */, true /* endTouch */, units.gu(10), 50);
192−
193− tryCompare(ApplicationManager, "focusedApplicationId", activeApp.appId)
194− }
195−
196202 function test_enterSpread_data() {
197203 return [
198− {tag: "<position1 (linear movement)", positionMarker: "positionMarker1", linear: true, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
199− {tag: "<position1 (non-linear movement)", positionMarker: "positionMarker1", linear: false, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 0 },
200− {tag: ">position1", positionMarker: "positionMarker1", linear: true, offset: +5, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
201− {tag: "<position2 (linear)", positionMarker: "positionMarker2", linear: true, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
202− {tag: "<position2 (non-linear)", positionMarker: "positionMarker2", linear: false, offset: 0, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
203− {tag: ">position2", positionMarker: "positionMarker2", linear: true, offset: +5, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
204− {tag: "<position3", positionMarker: "positionMarker3", linear: true, offset: 0, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
205− {tag: ">position3", positionMarker: "positionMarker3", linear: true, offset: +5, endPhase: 1, targetPhase: 2, newFocusedIndex: 2 },
204+ {tag: "<breakPoint (trigger)", progress: .2, cancel: false, endState: "staged", newFocusedIndex: 1 },
205+ {tag: "<breakPoint (cancel)", progress: .2, cancel: true, endState: "staged", newFocusedIndex: 0 },
206+ {tag: ">breakPoint (trigger)", progress: .5, cancel: false, endState: "spread", newFocusedIndex: 0 },
207+ {tag: ">breakPoint (cancel)", progress: .8, cancel: true, endState: "staged", newFocusedIndex: 0 },
206208 ];
207209 }
208210
209211 function test_enterSpread(data) {
210212 addApps(5)
211213
212− var spreadView = findChild(phoneStage, "spreadView");
213−
214− // Keep it inside the PhoneStage otherwise the controls on the right side will
214+ // Keep it inside the Stage otherwise the controls on the right side will
215215 // capture the press thus the "- 2" on startX.
216− var startX = phoneStage.width - 2;
217− var startY = phoneStage.height / 2;
216+ var startX = stage.width - 2;
217+ var startY = stage.height / 2;
218218 var endY = startY;
219− var endX = spreadView.width - (spreadView.width * spreadView[data.positionMarker]) - data.offset
220− - phoneStage.dragAreaWidth;
219+ var endX = stage.width - (stage.width * data.progress) - stage.dragAreaWidth;
221220
222221 var oldFocusedApp = ApplicationManager.get(0);
223222 var newFocusedApp = ApplicationManager.get(data.newFocusedIndex);
224223
225− touchFlick(phoneStage, startX, startY, endX, endY,
224+ touchFlick(stage, startX, startY, endX, endY,
226225 true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
227226
228− tryCompare(spreadView, "phase", data.endPhase)
229−
230− if (!data.linear) {
231− touchFlick(phoneStage, endX, endY, endX + units.gu(.5), endY,
232− false /* beginTouch */, false /* endTouch */, units.gu(10), 50);
233− touchFlick(phoneStage, endY + units.gu(.5), endY, endX, endY,
234− false /* beginTouch */, false /* endTouch */, units.gu(10), 50);
235− }
236−
237− touchRelease(phoneStage, endX, endY);
238−
239− tryCompare(spreadView, "phase", data.targetPhase)
240−
241− if (data.targetPhase == 2) {
242− var app2 = findChild(spreadView, "spreadDelegate_" + topLevelSurfaceList.idAt(2));
243− tryCompare(app2, "swipeToCloseEnabled", true);
244− mouseClick(app2, units.gu(1), units.gu(1));
245− }
227+ if (data.cancel) {
228+ touchFlick(stage, endX, endY, endX + units.gu(5), endY,
229+ false /* beginTouch */, true /* endTouch */, units.gu(10), 50);
230+ } else {
231+ touchRelease(stage, endX, endY); }
246232
247− tryCompare(ApplicationManager, "focusedApplicationId", newFocusedApp.appId);
233+ tryCompare(stage, "state", data.endState);
234+ tryCompare(ApplicationManager, "focusedApplicationId", data.endState == "spread" ? oldFocusedApp.appId : newFocusedApp.appId);
248235 }
249236
250237 function test_selectAppFromSpread_data() {
@@ -263,32 +250,26 @@ Item {
263250 function test_selectAppFromSpread(data) {
264251 addApps(data.total)
265252
266− var spreadView = findChild(phoneStage, "spreadView");
267−
268253 performEdgeSwipeToShowAppSpread();
269254
270− tryCompare(spreadView, "phase", 2);
271−
272− var tile = findChild(spreadView, "spreadDelegate_" + topLevelSurfaceList.idAt(data.index));
255+ var tile = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(data.index));
273256 var appId = ApplicationManager.get(data.index).appId;
274257
275− if (tile.mapToItem(spreadView, 0, 0).x > spreadView.width) {
258+ if (tile.mapToItem(stage, 0, 0).x > stage.width - units.gu(3)) {
276259 // Item is not visible... Need to flick the spread
277− var startX = phoneStage.width - units.gu(1);
278− var startY = phoneStage.height / 2;
260+ var startX = stage.width - units.gu(1);
261+ var startY = stage.height / 2;
279262 var endY = startY;
280263 var endX = units.gu(2);
281− touchFlick(phoneStage, startX, startY, endX, endY, true, true, units.gu(10), 50)
282− tryCompare(spreadView, "flicking", false);
283− tryCompare(spreadView, "moving", false);
284−// waitForRendering(phoneStage);
264+ touchFlick(stage, startX, startY, endX, endY, true, true, units.gu(10), 50)
285265 }
286266
287267 console.log("clicking app", data.index, "(", appId, ")")
288− tryCompare(tile, "swipeToCloseEnabled", true);
289− mouseClick(spreadView, tile.mapToItem(spreadView, 0, 0).x + units.gu(1), spreadView.height / 2)
268+ var dragArea = findChild(tile, "dragArea");
269+ tryCompare(dragArea, "closeable", true);
270+ mouseClick(stage, tile.mapToItem(stage, 0, 0).x + units.gu(1), stage.height / 2)
290271 tryCompare(ApplicationManager, "focusedApplicationId", appId);
291− tryCompare(spreadView, "phase", 0);
272+ tryCompare(stage, "state", "staged");
292273 }
293274
294275 function test_select_data() {
@@ -302,16 +283,22 @@ Item {
302283 function test_select(data) {
303284 addApps(5);
304285
305− var spreadView = findChild(phoneStage, "spreadView");
306286 var selectedApp = ApplicationManager.get(data.index);
287+ var appRepeater = findChild(stage, "appRepeater");
288+ var selectedAppDeleage = appRepeater.itemAt(data.index);
307289
308290 performEdgeSwipeToShowAppSpread();
309291
310− phoneStage.select(selectedApp.appId);
292+ print("tapping", selectedAppDeleage.appId, selectedAppDeleage.visible)
293+ if (selectedAppDeleage.x > stage.width - units.gu(5)) {
294+ touchFlick(stage, stage.width - units.gu(2), stage.height / 2, units.gu(2), stage.height / 2, true, true, units.gu(2), 10)
295+ }
296+
297+ tap(selectedAppDeleage, 1, 1);
311298
312− tryCompare(spreadView, "contentX", -spreadView.shift);
299+ tryCompare(stage, "state", "staged");
313300
314− compare(ApplicationManager.focusedApplicationId, selectedApp.appId);
301+ tryCompare(ApplicationManager, "focusedApplicationId", selectedApp.appId);
315302 }
316303
317304 function test_backgroundClickCancelsSpread() {
@@ -320,37 +307,14 @@ Item {
320307 var focusedAppId = ApplicationManager.focusedApplicationId;
321308
322309 performEdgeSwipeToShowAppSpread();
310+ tryCompare(stage, "state", "spread");
323311
324− mouseClick(phoneStage, units.gu(1), units.gu(1));
312+ mouseClick(stage, units.gu(1), units.gu(1));
325313
326− // Make sure the spread is in the idle position
327− var spreadView = findChild(phoneStage, "spreadView");
328− tryCompare(spreadView, "contentX", -spreadView.shift);
314+ tryCompare(stage, "state", "staged");
329315
330316 // Make sure the same app is still focused
331− compare(focusedAppId, ApplicationManager.focusedApplicationId);
332− }
333−
334− function init() {
335− // wait until unity8-dash is up and running.
336− // it's started automatically by ApplicationManager mock implementation
337− tryCompare(ApplicationManager, "count", 1);
338− var dashApp = ApplicationManager.findApplication("unity8-dash");
339− verify(dashApp);
340− tryCompare(dashApp, "state", ApplicationInfoInterface.Running);
341− }
342−
343− function cleanup() {
344− killApps();
345−
346− phoneStage.shellOrientationAngle = 0;
347− phoneStage.select(ApplicationManager.get(0).appId);
348−
349− // wait for PhoneStage to stabilize back into its initial state
350− var spreadView = findChild(phoneStage, "spreadView");
351− while (spreadView.phase !== 0 || spreadView.contentX !== -spreadView.shift || spreadView.selectedIndex != -1) {
352− wait(50);
353− }
317+ tryCompare(ApplicationManager, "focusedApplicationId", focusedAppId);
354318 }
355319
356320 function test_focusNewTopMostAppAfterFocusedOneClosesItself() {
@@ -370,60 +334,9 @@ Item {
370334 tryCompare(firstApp, "focused", true);
371335 }
372336
373− function test_cantCloseWhileSnapping() {
374− addApps(2);
375−
376− performEdgeSwipeToShowAppSpread();
377−
378− var spreadView = findChild(phoneStage, "spreadView");
379− var selectedApp = ApplicationManager.get(2);
380−
381− performEdgeSwipeToShowAppSpread();
382−
383− var app0 = findChild(spreadView, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
384− var app1 = findChild(spreadView, "spreadDelegate_" + topLevelSurfaceList.idAt(1));
385− var app2 = findChild(spreadView, "spreadDelegate_" + topLevelSurfaceList.idAt(2));
386−
387− var dragArea0 = findChild(app0, "dragArea");
388− var dragArea1 = findChild(app1, "dragArea");
389− var dragArea2 = findChild(app2, "dragArea");
390−
391− compare(dragArea0.enabled, true);
392− compare(dragArea1.enabled, true);
393− compare(dragArea2.enabled, true);
394−
395− phoneStage.select(selectedApp.appId);
396−
397− // Make sure all drag areas are disabled instantly. Don't use tryCompare here!
398− compare(dragArea0.enabled, false);
399− compare(dragArea1.enabled, false);
400− compare(dragArea2.enabled, false);
401−
402− tryCompare(spreadView, "contentX", -spreadView.shift)
403− }
404−
405− function test_cantAccessPhoneStageWhileRightEdgeGesture() {
406− var spreadView = findChild(phoneStage, "spreadView");
407− var eventEaterArea = findChild(phoneStage, "eventEaterArea")
408−
409− var startX = phoneStage.width - 2;
410− var startY = phoneStage.height / 2;
411− var endY = startY;
412− var endX = phoneStage.width / 2;
413−
414− touchFlick(phoneStage, startX, startY, endX, endY,
415− true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
416−
417− compare(eventEaterArea.enabled, true);
418−
419− touchRelease(phoneStage, endX, endY);
420−
421− compare(eventEaterArea.enabled, false);
422− }
423−
424337 function test_leftEdge_data() {
425338 return [
426− { tag: "normal", inSpread: false, leftEdgeDragWidth: units.gu(5), shouldMoveApp: true },
339+ { tag: "normal", inSpread: false, leftEdgeDragWidth: units.gu(20), shouldMoveApp: true },
427340 { tag: "inSpread", inSpread: true, leftEdgeDragWidth: units.gu(5), shouldMoveApp: false }
428341 ]
429342 }
@@ -435,22 +348,24 @@ Item {
435348 performEdgeSwipeToShowAppSpread();
436349 }
437350
438− var focusedDelegate = findChild(phoneStage, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
439− phoneStage.inverseProgress = data.leftEdgeDragWidth;
351+ var focusedDelegate = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(0));
352+ var currentX = focusedDelegate.x;
353+
354+ stage.leftEdgeDragProgress = data.leftEdgeDragWidth;
440355
441− tryCompare(focusedDelegate, "x", data.shouldMoveApp ? data.leftEdgeDragWidth : 0);
356+ tryCompare(focusedDelegate, "x", data.shouldMoveApp ? data.leftEdgeDragWidth : currentX);
442357
443− phoneStage.inverseProgress = 0;
358+ stage.leftEdgeDragProgress = 0;
444359
445− tryCompare(focusedDelegate, "x", 0);
360+ tryCompare(focusedDelegate, "x", currentX);
446361 }
447362
448363 function test_focusedAppIsTheOnlyRunningApp() {
449364 addApps(2);
450365
451− var delegateA = findChild(phoneStage, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
366+ var delegateA = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(0));
452367 verify(delegateA);
453− var delegateB = findChild(phoneStage, "spreadDelegate_" + topLevelSurfaceList.idAt(1));
368+ var delegateB = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(1));
454369 verify(delegateB);
455370
456371 // A is focused and running, B is unfocused and suspended
@@ -461,7 +376,7 @@ Item {
461376
462377 // Switch foreground/focused appp from A to B
463378 performEdgeSwipeToShowAppSpread();
464− phoneStage.select(delegateB.application.appId);
379+ tap(delegateB, 1, 1);
465380
466381 // Now it's the other way round
467382 // A is unfocused and suspended, B is focused and running
@@ -474,15 +389,15 @@ Item {
474389 function test_dashRemainsRunningIfStageIsToldSo() {
475390 addApps(1);
476391
477− var delegateDash = findChild(phoneStage, "spreadDelegate_" + topLevelSurfaceList.idAt(1));
392+ var delegateDash = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(1));
478393 verify(delegateDash);
479394 compare(delegateDash.application.appId, "unity8-dash");
480395
481− var delegateOther = findChild(phoneStage, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
396+ var delegateOther = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(0));
482397 verify(delegateOther);
483398
484399 performEdgeSwipeToShowAppSpread();
485− phoneStage.select("unity8-dash");
400+ tap(delegateDash, 1, 1);
486401
487402 tryCompare(delegateDash, "focus", true);
488403 tryCompare(delegateDash.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
@@ -490,41 +405,40 @@ Item {
490405 compare(delegateOther.application.requestedState, ApplicationInfoInterface.RequestedSuspended);
491406
492407 performEdgeSwipeToShowAppSpread();
493− phoneStage.select(delegateOther.application.appId);
494−
408+ tap(delegateOther, 1, 1);
495409 // The other app gets focused and running but dash is kept running despite being unfocused
410+ tryCompare(delegateOther, "focus", true);
411+ tryCompare(delegateOther.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
496412 tryCompare(delegateDash, "focus", false);
497413 tryCompare(delegateDash.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
498− compare(delegateOther.focus, true);
499− compare(delegateOther.application.requestedState, ApplicationInfoInterface.RequestedRunning);
500414 }
501415
502416 function test_foregroundAppIsSuspendedWhenStageIsSuspended() {
503417 addApps(1);
504418
505− var delegate = findChild(phoneStage, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
419+ var delegate = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(0));
506420 verify(delegate);
507421
508422 compare(delegate.focus, true);
509423 compare(delegate.application.requestedState, ApplicationInfoInterface.RequestedRunning);
510424
511− phoneStage.suspended = true;
425+ stage.suspended = true;
512426
513427 tryCompare(delegate.application, "requestedState", ApplicationInfoInterface.RequestedSuspended);
514428
515− phoneStage.suspended = false;
429+ stage.suspended = false;
516430
517431 tryCompare(delegate.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
518432 }
519433
520434 function test_mouseEdgePush() {
521− var spreadView = findChild(phoneStage, "spreadView")
522435 addApps(1);
523− mouseMove(phoneStage, phoneStage.width - 1, units.gu(10));
436+ mouseMove(stage, stage.width - 1, units.gu(10));
524437 for (var i = 0; i < units.gu(10); i++) {
525− phoneStage.pushRightEdge(1);
438+ stage.pushRightEdge(1);
526439 }
527− tryCompare(spreadView, "phase", 2);
440+ mouseMove(stage, stage.width - units.gu(5), units.gu(10));
441+ compare(stage.state, "spread");
528442 }
529443
530444 function test_closeSurfaceOfMultiSurfaceApp() {
@@ -539,19 +453,44 @@ Item {
539453
540454 performEdgeSwipeToShowAppSpread();
541455
542− var appDelegate = findChild(phoneStage, "spreadDelegate_" + surface1Id);
543− verify(appDelegate);
544− tryCompare(appDelegate, "swipeToCloseEnabled", true);
456+ var appDelegate = findChild(stage, "appDelegate_" + surface1Id);
457+ var dragArea = findChild(appDelegate, "dragArea")
458+ verify(dragArea);
459+ tryCompare(dragArea, "closeable", true);
545460
546461 compare(webbrowserApp.surfaceList.count, 2);
547− compare(webbrowserApp.state, ApplicationInfoInterface.Running);
548462
549463 swipeSurfaceUpwards(surface1Id);
550464
551465 // Surface must eventually be gone
552466 tryCompareFunction(function() { return topLevelSurfaceList.indexForId(surface1Id); }, -1);
553467 tryCompare(webbrowserApp.surfaceList, "count", 1);
554− compare(webbrowserApp.state, ApplicationInfoInterface.Running);
468+ }
469+
470+ function test_swipeToClose_data() {
471+ return [
472+ { tag: "closeable", closeable: true },
473+ { tag: "not closeable", closeable: false }
474+ ]
475+ }
476+
477+ function test_swipeToClose(data) {
478+ var surface1Id = topLevelSurfaceList.nextId;
479+ var webbrowserApp = ApplicationManager.startApplication("webbrowser-app");
480+ waitUntilAppSurfaceShowsUp(surface1Id);
481+
482+ performEdgeSwipeToShowAppSpread();
483+
484+ var appDelegate = findChild(stage, "appDelegate_" + surface1Id);
485+ var dragArea = findChild(appDelegate, "dragArea")
486+ verify(dragArea);
487+ dragArea.closeable = data.closeable;
488+
489+ var oldCount = ApplicationManager.count;
490+
491+ swipeSurfaceUpwards(surface1Id);
492+
493+ tryCompare(ApplicationManager, "count", data.closeable ? oldCount - 1 : oldCount);
555494 }
556495
557496 /*
@@ -632,11 +571,10 @@ Item {
632571
633572 performEdgeSwipeToShowAppSpread();
634573
635− {
636− var appDelegate = findChild(phoneStage, "spreadDelegate_" + webbrowserSurfaceId);
637− verify(appDelegate);
638− tryCompare(appDelegate, "swipeToCloseEnabled", true);
639− }
574+ var appDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
575+ var dragArea = findChild(appDelegate, "dragArea");
576+ verify(dragArea);
577+ tryCompare(dragArea, "closeable", true);
640578
641579 swipeSurfaceUpwards(webbrowserSurfaceId);
642580
tests/qmltests/Stages/tst_Splash.qml → tests/qmltests/Stage/tst_Splash.qmlrenamed+1−1
@@ -18,7 +18,7 @@ import QtQuick 2.4
1818import QtTest 1.0
1919import Unity.Test 0.1 as UT
2020import ".."
21−import "../../../qml/Stages"
21+import "../../../qml/Stage"
2222import Ubuntu.Components 1.3
2323import Unity.Application 0.1
2424
tests/qmltests/Stages/tst_SpreadDelegate.qml → tests/qmltests/Stage/tst_SpreadDelegate.qmlrenamed

The content is the same.

tests/qmltests/Stages/tst_SurfaceContainer.qml → tests/qmltests/Stage/tst_SurfaceContainer.qmlrenamed+2−2
@@ -20,7 +20,7 @@ import QtTest 1.0
2020import Unity.Test 0.1 as UT
2121import Ubuntu.Components 1.3
2222import Unity.Application 0.1
23−import "../../../qml/Stages"
23+import "../../../qml/Stage"
2424
2525Rectangle {
2626 color: "red"
@@ -87,7 +87,7 @@ Rectangle {
8787 }
8888 Row {
8989 CheckBox {id: promptCheckbox; checked: false; activeFocusOnPress: false
90− enabled: surfaceContainerLoader.item.surface === null }
90+ enabled: surfaceContainerLoader.item && surfaceContainerLoader.item.surface === null }
9191 Label { text: "isPromptSurface" }
9292 }
9393
tests/qmltests/Stages/tst_TabletStage.qml → tests/qmltests/Stage/tst_TabletStage.qmlrenamed+107−241
@@ -24,53 +24,35 @@ import Utils 0.1
2424import WindowManager 0.1
2525
2626import ".."
27−import "../../../qml/Stages"
27+import "../../../qml/Stage"
2828import "../../../qml/Components"
2929
3030Rectangle {
3131 id: root
3232 color: "grey"
33− width: tabletStageLoader.width + controls.width
34− height: tabletStageLoader.height
33+ width: units.gu(160*0.7)
34+ height: units.gu(100*0.7)
3535
3636 property var greeter: { fullyShown: true }
3737
38− TopLevelSurfaceList {
39− id: topSurfaceList
40− applicationsModel: ApplicationManager
41− }
42−
43− Loader {
44− id: tabletStageLoader
45−
46− x: ((root.width - controls.width) - width) / 2
47− y: (root.height - height) / 2
48− width: units.gu(160*0.7)
49− height: units.gu(100*0.7)
50−
38+ Stage {
39+ id: stage
40+ anchors { fill: parent; rightMargin: units.gu(30) }
41+ dragAreaWidth: units.gu(2)
42+ interactive: true
43+ shellOrientation: Qt.LandscapeOrientation
44+ nativeWidth: width
45+ nativeHeight: height
46+ orientations: Orientations {
47+ native_: Qt.LandscapeOrientation
48+ primary: Qt.LandscapeOrientation
49+ }
5150 focus: true
52−
53− property bool itemDestroyed: false
54− sourceComponent: Component {
55− TabletStage {
56− anchors.fill: parent
57− Component.onDestruction: {
58− tabletStageLoader.itemDestroyed = true;
59− }
60− dragAreaWidth: units.gu(2)
61− maximizedAppTopMargin: units.gu(3)
62− interactive: true
63− shellOrientation: Qt.LandscapeOrientation
64− nativeWidth: width
65− nativeHeight: height
66− orientations: Orientations {
67− native_: Qt.LandscapeOrientation
68− primary: Qt.LandscapeOrientation
69− }
70− focus: true
71− applicationManager: ApplicationManager
72− topLevelSurfaceList: topSurfaceList
73− }
51+ mode: "stagedWithSideStage"
52+ applicationManager: ApplicationManager
53+ topLevelSurfaceList: TopLevelSurfaceList {
54+ id: topLevelSurfaceList
55+ applicationsModel: ApplicationManager
7456 }
7557 }
7658
@@ -84,7 +66,7 @@ Rectangle {
8466 right: parent.right
8567 }
8668
87− // don't let mouse clicks in the controls area disturb the TabletStage behind it
69+ // don't let mouse clicks in the controls area disturb the Stage behind it
8870 MouseArea { anchors.fill: parent }
8971
9072 Column {
@@ -95,15 +77,16 @@ Rectangle {
9577 id: edgeBarrierControls
9678 text: "Drag here to pull out spread"
9779 backgroundColor: "blue"
98− onDragged: { tabletStageLoader.item.pushRightEdge(amount); }
80+ onDragged: { stage.pushRightEdge(amount); }
9981 Component.onCompleted: {
100− edgeBarrierControls.target = testCase.findChild(tabletStageLoader, "edgeBarrierController");
82+ edgeBarrierControls.target = testCase.findChild(stage, "edgeBarrierController");
10183 }
10284 }
10385
10486 Button {
10587 text: testCase.sideStage ? testCase.sideStage.shown ? "Hide Side-stage" : "Show Side-stage" : ""
10688 enabled: testCase.sideStage
89+ activeFocusOnPress: false
10790 onClicked: {
10891 if (testCase.sideStage.shown) testCase.sideStage.hide();
10992 else testCase.sideStage.show();
@@ -140,46 +123,50 @@ Rectangle {
140123 name: "TabletStage"
141124 when: windowShown
142125
143− property Item tabletStage: tabletStageLoader.status === Loader.Ready ? tabletStageLoader.item : null
144− property Item sideStage: tabletStage ? findChild(tabletStage, "sideStage") : null
126+ readonly property alias topSurfaceList: stage.topLevelSurfaceList
127+ property Item sideStage: stage ? findChild(stage, "sideStage") : null
145128
146129 function init() {
147130 stageSaver.clear();
148− tabletStageLoader.active = true;
149− tryCompare(tabletStageLoader, "status", Loader.Ready);
150131
151132 tryCompare(topSurfaceList, "count", 1);
152133 compare(topSurfaceList.applicationAt(0).appId, "unity8-dash");
153134
154135 // this is very strange, but sometimes the test starts without
155− // TabletStage components having finished loading themselves
136+ // Stage components having finished loading themselves
156137 var appWindow = null
157138 while (!appWindow) {
158139 appWindow = findAppWindowForSurfaceId(topSurfaceList.idAt(0));
159140 if (!appWindow) {
160− console.log("didn't find unity8-dash appWindow in " + tabletStage + ". Trying again...");
141+ console.log("didn't find unity8-dash appWindow in Stage. Trying again...");
161142 wait(50);
162143 }
163144 }
164145
165146 waitUntilAppSurfaceShowsUp(topSurfaceList.idAt(0));
166147 sideStage.hideNow()
148+ tryCompare(sideStage, "x", stage.width)
167149 }
168150
169151 function cleanup() {
170− tabletStageLoader.itemDestroyed = false;
171− tabletStageLoader.active = false;
172−
173− tryCompare(tabletStageLoader, "status", Loader.Null);
174− tryCompare(tabletStageLoader, "item", null);
175− // Loader.status might be Loader.Null and Loader.item might be null but the Loader
176− // actually took place. Likely because Loader waits until the next event loop
177− // iteration to do its work. So to ensure the reload, we will wait until the
178− // Shell instance gets destroyed.
179− tryCompare(tabletStageLoader, "itemDestroyed", true);
152+ ApplicationManager.requestFocusApplication("unity8-dash");
153+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
154+ tryCompare(stage, "state", "stagedWithSideStage");
155+ waitForRendering(stage);
180156
181157 killApps();
158+
159+ // wait for Stage to stabilize back into its initial state
160+ var appRepeater = findChild(stage, "appRepeater");
161+ tryCompare(appRepeater, "count", 1);
162+ tryCompare(appRepeater.itemAt(0), "x", 0);
163+
164+ sideStage.hideNow();
165+ tryCompare(sideStage, "x", stage.width)
166+ waitForRendering(stage)
167+
182168 WindowStateStorage.clear();
169+ wait(100)
183170 }
184171
185172 function waitUntilAppSurfaceShowsUp(surfaceId) {
@@ -191,7 +178,7 @@ Rectangle {
191178 }
192179
193180 function findAppWindowForSurfaceId(surfaceId) {
194− var spreadDelegate = findChild(tabletStage, "spreadDelegate_" + surfaceId);
181+ var spreadDelegate = findChild(stage, "appDelegate_" + surfaceId);
195182 if (!spreadDelegate) {
196183 return null;
197184 }
@@ -210,7 +197,7 @@ Rectangle {
210197
211198 function waitUntilAppDelegateStopsMoving(targetSurfaceId)
212199 {
213− var targetAppDelegate = findChild(tabletStage, "spreadDelegate_" + targetSurfaceId);
200+ var targetAppDelegate = findChild(stage, "appDelegate_" + targetSurfaceId);
214201 verify(targetAppDelegate);
215202 var lastValue = undefined;
216203 do {
@@ -220,15 +207,9 @@ Rectangle {
220207 }
221208
222209 function performEdgeSwipeToShowAppSpread() {
223− touchFlick(tabletStage,
224− tabletStage.width - (tabletStage.dragAreaWidth / 2), tabletStage.height / 2,
225− tabletStage.x + 1, tabletStage.height / 2);
210+ touchFlick(stage, stage.width - (stage.dragAreaWidth / 2), stage.height / 2, stage.x + 1, stage.height / 2);
226211
227− var spreadView = findChild(tabletStage, "spreadView");
228− verify(spreadView);
229− tryCompare(spreadView, "phase", 2);
230− tryCompare(spreadView, "flicking", false);
231− tryCompare(spreadView, "moving", false);
212+ tryCompare(stage, "state", "spread");
232213 }
233214
234215 function swipeSurfaceUpwards(surfaceId) {
@@ -244,17 +225,12 @@ Rectangle {
244225
245226 function dragToSideStage(surfaceId) {
246227 sideStage.showNow();
247− var targetAppDelegate = findChild(tabletStage, "spreadDelegate_" + surfaceId);
228+ var targetAppDelegate = findChild(stage, "appDelegate_" + surfaceId);
248229
249− var pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
250− var end_pos = tabletStage.width - sideStage.width / 2;
230+ var pos = stage.width - sideStage.width - (stage.width - sideStage.width) / 2;
231+ var end_pos = stage.width - sideStage.width / 2;
251232
252− multiTouchDragUntil([0,1,2],
253− tabletStage,
254− pos,
255− tabletStage.height / 2,
256− units.gu(3),
257− 0,
233+ multiTouchDragUntil([0,1,2], stage, pos, stage.height / 2, units.gu(3), 0,
258234 function() {
259235 pos += units.gu(3);
260236 return sideStage.shown && !sideStage.showAnimation.running &&
@@ -265,16 +241,16 @@ Rectangle {
265241
266242 function dragToMainStage(surfaceId) {
267243 sideStage.showNow();
268− var targetAppDelegate = findChild(tabletStage, "spreadDelegate_" + surfaceId);
244+ var targetAppDelegate = findChild(stage, "appDelegate_" + surfaceId);
269245 verify(targetAppDelegate);
270246
271− var pos = tabletStage.width - sideStage.width / 2;
272− var end_pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
247+ var pos = stage.width - sideStage.width / 2;
248+ var end_pos = stage.width - sideStage.width - (stage.width - sideStage.width) / 2;
273249
274250 multiTouchDragUntil([0,1,2],
275− tabletStage,
251+ stage,
276252 pos,
277− tabletStage.height / 2,
253+ stage.height / 2,
278254 -units.gu(3),
279255 0,
280256 function() {
@@ -290,7 +266,7 @@ Rectangle {
290266 var webbrowserSurfaceId = topSurfaceList.nextId;
291267 webbrowserCheckBox.checked = true;
292268 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
293− var webbrowserDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
269+ var webbrowserDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
294270 verify(webbrowserDelegate);
295271 compare(webbrowserDelegate.stage, ApplicationInfoInterface.MainStage);
296272 var webbrowserWindow = findAppWindowForSurfaceId(webbrowserSurfaceId);
@@ -303,7 +279,7 @@ Rectangle {
303279 waitUntilAppSurfaceShowsUp(dialerSurfaceId);
304280
305281 var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
306− var dialerDelegate = findChild(tabletStage, "spreadDelegate_" + dialerSurfaceId);
282+ var dialerDelegate = findChild(stage, "appDelegate_" + dialerSurfaceId);
307283 verify(dialerDelegate);
308284 compare(dialerDelegate.stage, ApplicationInfoInterface.SideStage);
309285
@@ -336,17 +312,18 @@ Rectangle {
336312
337313 performEdgeSwipeToShowAppSpread();
338314
339− var appDelegate = findChild(tabletStage, "spreadDelegate_" + dialerSurfaceId);
340− verify(appDelegate);
315+ var appDelegate = findChild(stage, "appDelegate_" + dialerSurfaceId);
316+ var dragArea = findChild(appDelegate, "dragArea");
317+ verify(dragArea);
341318 compare(appDelegate.stage, ApplicationInfoInterface.SideStage);
342− tryCompare(appDelegate, "swipeToCloseEnabled", true);
319+ tryCompare(dragArea, "closeable", true);
343320
344321 swipeSurfaceUpwards(dialerSurfaceId);
345322
346323 // Check that dialer-app has been closed
347324
348325 tryCompareFunction(function() {
349− return findChild(tabletStage, "appWindow_" + dialerCheckBox.appId);
326+ return findChild(stage, "appWindow_" + dialerCheckBox.appId);
350327 }, null);
351328
352329 tryCompareFunction(function() {
@@ -359,7 +336,7 @@ Rectangle {
359336 webbrowserCheckBox.checked = true;
360337 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
361338 var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
362− var webbrowserDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
339+ var webbrowserDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
363340 compare(webbrowserDelegate.stage, ApplicationInfoInterface.MainStage);
364341
365342 tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Running);
@@ -368,7 +345,7 @@ Rectangle {
368345 galleryCheckBox.checked = true;
369346 waitUntilAppSurfaceShowsUp(gallerySurfaceId);
370347 var galleryApp = ApplicationManager.findApplication(galleryCheckBox.appId);
371− var galleryDelegate = findChild(tabletStage, "spreadDelegate_" + gallerySurfaceId);
348+ var galleryDelegate = findChild(stage, "appDelegate_" + gallerySurfaceId);
372349 compare(galleryDelegate.stage, ApplicationInfoInterface.MainStage);
373350
374351 tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
@@ -389,7 +366,7 @@ Rectangle {
389366 WindowStateStorage.saveStage(facebookCheckBox.appId, ApplicationInfoInterface.SideStage)
390367 WindowStateStorage.saveStage(dialerCheckBox.appId, ApplicationInfoInterface.SideStage)
391368
392− var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
369+ var stagesPriv = findInvisibleChild(stage, "DesktopStagePrivate");
393370 verify(stagesPriv);
394371
395372 // launch two main stage apps
@@ -398,7 +375,7 @@ Rectangle {
398375 var webbrowserSurfaceId = topSurfaceList.nextId;
399376 webbrowserCheckBox.checked = true;
400377 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
401− var webbrowserDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
378+ var webbrowserDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
402379 verify(webbrowserDelegate);
403380 compare(webbrowserDelegate.stage, ApplicationInfoInterface.MainStage);
404381 var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
@@ -407,7 +384,7 @@ Rectangle {
407384 galleryCheckBox.checked = true;
408385 waitUntilAppSurfaceShowsUp(gallerySurfaceId);
409386 var galleryApp = ApplicationManager.findApplication(galleryCheckBox.appId);
410− var galleryDelegate = findChild(tabletStage, "spreadDelegate_" + gallerySurfaceId);
387+ var galleryDelegate = findChild(stage, "appDelegate_" + gallerySurfaceId);
411388 compare(galleryDelegate.stage, ApplicationInfoInterface.MainStage);
412389
413390 compare(stagesPriv.mainStageAppId, galleryCheckBox.appId);
@@ -419,14 +396,14 @@ Rectangle {
419396 dialerCheckBox.checked = true;
420397 waitUntilAppSurfaceShowsUp(dialerSurfaceId);
421398 var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
422− var dialerDelegate = findChild(tabletStage, "spreadDelegate_" + dialerSurfaceId);
399+ var dialerDelegate = findChild(stage, "appDelegate_" + dialerSurfaceId);
423400 compare(dialerDelegate.stage, ApplicationInfoInterface.SideStage);
424401
425402 var facebookSurfaceId = topSurfaceList.nextId;
426403 facebookCheckBox.checked = true;
427404 waitUntilAppSurfaceShowsUp(facebookSurfaceId);
428405 var facebookApp = ApplicationManager.findApplication(facebookCheckBox.appId);
429− var facebookDelegate = findChild(tabletStage, "spreadDelegate_" + facebookSurfaceId);
406+ var facebookDelegate = findChild(stage, "appDelegate_" + facebookSurfaceId);
430407 compare(facebookDelegate.stage, ApplicationInfoInterface.SideStage);
431408
432409 compare(stagesPriv.sideStageAppId, facebookCheckBox.appId);
@@ -461,40 +438,31 @@ Rectangle {
461438 webbrowserCheckBox.checked = true;
462439 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
463440 var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
464− var webbrowserDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
441+ var webbrowserDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
465442 compare(webbrowserDelegate.stage, ApplicationInfoInterface.MainStage);
466443
467444 var dialerSurfaceId = topSurfaceList.nextId;
468445 dialerCheckBox.checked = true;
469446 waitUntilAppSurfaceShowsUp(dialerSurfaceId);
470447 var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
471− var dialerDelegate = findChild(tabletStage, "spreadDelegate_" + dialerSurfaceId);
448+ var dialerDelegate = findChild(stage, "appDelegate_" + dialerSurfaceId);
472449 compare(dialerDelegate.stage, ApplicationInfoInterface.SideStage);
473450
474451
475452 compare(webbrowserApp.requestedState, ApplicationInfoInterface.RequestedRunning);
476453 compare(dialerApp.requestedState, ApplicationInfoInterface.RequestedRunning);
477454
478− tabletStage.suspended = true;
455+ stage.suspended = true;
479456
480457 tryCompare(webbrowserApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
481458 tryCompare(dialerApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
482459
483− tabletStage.suspended = false;
460+ stage.suspended = false;
484461
485462 tryCompare(webbrowserApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
486463 tryCompare(dialerApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
487464 }
488465
489− function test_mouseEdgePush() {
490− var spreadView = findChild(tabletStageLoader, "spreadView")
491− mouseMove(tabletStageLoader, tabletStageLoader.width - 1, units.gu(10));
492− for (var i = 0; i < units.gu(10); i++) {
493− tabletStageLoader.item.pushRightEdge(1);
494− }
495− tryCompare(spreadView, "phase", 2);
496− }
497−
498466 function test_threeFingerTapOpensSideStage_data() {
499467 return [
500468 { tag: "1 finger", touchIds: [0], result: false },
@@ -505,7 +473,7 @@ Rectangle {
505473 }
506474
507475 function test_threeFingerTapOpensSideStage(data) {
508− multiTouchTap(data.touchIds, tabletStage, tabletStage.width / 2, tabletStage.height / 2);
476+ multiTouchTap(data.touchIds, stage, stage.width / 2, stage.height / 2);
509477 wait(200);
510478 tryCompare(sideStage, "shown", data.result);
511479 }
@@ -523,18 +491,13 @@ Rectangle {
523491 function test_threeFingerTapClosesSideStage(data) {
524492 sideStage.showNow();
525493
526− multiTouchTap(data.touchIds, tabletStage, tabletStage.width / 2, tabletStage.height / 2);
494+ multiTouchTap(data.touchIds, stage, stage.width / 2, stage.height / 2);
527495 wait(200);
528496 tryCompare(sideStage, "shown", data.result);
529497 }
530498
531499 function test_threeFingerDragOpensSidestage() {
532− multiTouchDragUntil([0,1,2],
533− tabletStage,
534− tabletStage.width / 4,
535− tabletStage.height / 4,
536− units.gu(1),
537− 0,
500+ multiTouchDragUntil([0,1,2], stage, stage.width / 4, stage.height / 4, units.gu(1), 0,
538501 function() { return sideStage.shown; });
539502 }
540503
@@ -546,7 +509,7 @@ Rectangle {
546509 }
547510
548511 function test_applicationLoadsInDefaultStage(data) {
549− var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
512+ var stagesPriv = findInvisibleChild(stage, "DesktopStagePrivate");
550513 verify(stagesPriv);
551514
552515 tryCompare(stagesPriv, "mainStageAppId", "unity8-dash");
@@ -570,7 +533,7 @@ Rectangle {
570533 function test_applicationLoadsInSavedStage(data) {
571534 WindowStateStorage.saveStage(webbrowserCheckBox.appId, data.stage)
572535
573− var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
536+ var stagesPriv = findInvisibleChild(stage, "DesktopStagePrivate");
574537 verify(stagesPriv);
575538
576539 tryCompare(stagesPriv, "mainStageAppId", "unity8-dash");
@@ -595,7 +558,7 @@ Rectangle {
595558 WindowStateStorage.saveStage(webbrowserCheckBox.appId, data.fromStage);
596559 stageSaver.clear();
597560
598− var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
561+ var stagesPriv = findInvisibleChild(stage, "DesktopStagePrivate");
599562 verify(stagesPriv);
600563
601564 tryCompare(stagesPriv, "mainStageAppId", "unity8-dash");
@@ -622,63 +585,37 @@ Rectangle {
622585 webbrowserCheckBox.checked = true;
623586 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
624587
625− var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
588+ var appDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
626589 verify(appDelegate);
627− compare(appDelegate.stage, ApplicationInfoInterface.MainStage);
590+ waitForRendering(stage);
591+ tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
628592
629593 dragToSideStage(webbrowserSurfaceId);
630594
631− var spreadView = findChild(tabletStageLoader, "spreadView")
632− tryCompare(spreadView, "surfaceDragging", false);
633595 tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
634596 }
635597
636− function test_unloadSideStageByDraggingFromStageStage() {
598+ function test_unloadSideStageByDraggingFromSideStage() {
637599 sideStage.showNow();
638600 WindowStateStorage.saveStage(webbrowserCheckBox.appId, ApplicationInfoInterface.SideStage)
601+ // WindowStateStorage is async... Lets wait for it to be fully processed...
602+ tryCompareFunction(function() {
603+ return WindowStateStorage.getStage(webbrowserCheckBox.appId, ApplicationInfoInterface.MainStage) === ApplicationInfoInterface.SideStage
604+ }, true);
639605 var webbrowserSurfaceId = topSurfaceList.nextId;
640606 webbrowserCheckBox.checked = true;
641607 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
642608
643− var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
609+ var appDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
644610 verify(appDelegate);
645− compare(appDelegate.stage, ApplicationInfoInterface.SideStage);
611+ waitForRendering(stage);
612+ tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
646613
647614 dragToMainStage(webbrowserSurfaceId);
648615
649− var spreadView = findChild(tabletStageLoader, "spreadView")
650− tryCompare(spreadView, "surfaceDragging", false);
651616 tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
652617 }
653618
654− function test_closeSurfaceOfMultiSurfaceApp() {
655− var surface1Id = topSurfaceList.nextId;
656− webbrowserCheckBox.checked = true;
657− waitUntilAppSurfaceShowsUp(surface1Id);
658− var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
659−
660− var surface2Id = topSurfaceList.nextId;
661− verify(surface1Id !== surface2Id); // sanity checking
662− webbrowserCheckBox.createSurface();
663− waitUntilAppSurfaceShowsUp(surface2Id);
664−
665− performEdgeSwipeToShowAppSpread();
666−
667− var appDelegate = findChild(tabletStage, "spreadDelegate_" + surface1Id);
668− verify(appDelegate);
669− tryCompare(appDelegate, "swipeToCloseEnabled", true);
670−
671− compare(webbrowserApp.surfaceList.count, 2);
672− compare(webbrowserApp.state, ApplicationInfoInterface.Running);
673−
674− swipeSurfaceUpwards(surface1Id);
675−
676− // Surface must eventually be gone
677− tryCompareFunction(function() { return topSurfaceList.indexForId(surface1Id); }, -1);
678− tryCompare(webbrowserApp.surfaceList, "count", 1);
679− compare(webbrowserApp.state, ApplicationInfoInterface.Running);
680− }
681−
682619 /*
683620 1- Suspended app gets killed behind the scenes, causing its surface to go zombie.
684621 2- Surface gets screenshotted and removed. Its slot in the topSurfaceList remains,
@@ -724,64 +661,12 @@ Rectangle {
724661 compare(webbrowserApp.surfaceList.count, 1);
725662 }
726663
727− /*
728− 1 - user suspends an application (ie, focus a surface from a different application)
729− 2 - That suspended application gets killed, causing its surface to go zombie
730− 3 - user goes to spread and closes that zombie surface. Actually, by that time
731− Tablet shell will be displaying a screenshot of it instead, so there's no
732− MirSurface whatsoever backing it up.
733− */
734− function test_closeZombieSurface()
735− {
736− compare(topSurfaceList.applicationAt(0).appId, "unity8-dash");
737− var dashSurfaceId = topSurfaceList.idAt(0);
738− var dashSurface = topSurfaceList.surfaceAt(0);
739−
740− var webbrowserSurfaceId = topSurfaceList.nextId;
741− webbrowserCheckBox.checked = true;
742− waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
743− var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
744−
745− switchToSurface(dashSurfaceId);
746−
747− tryCompare(MirFocusController, "focusedSurface", dashSurface);
748− tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
749−
750− compare(webbrowserApp.surfaceList.count, 1);
751−
752− // simulate the suspended app being killed by the out-of-memory daemon
753− webbrowserApp.surfaceList.get(0).setLive(false);
754−
755− // wait until the surface is gone
756− tryCompare(webbrowserApp.surfaceList, "count", 0);
757− compare(topSurfaceList.surfaceAt(topSurfaceList.indexForId(webbrowserSurfaceId)), null);
758−
759− performEdgeSwipeToShowAppSpread();
760−
761− {
762− var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
763− verify(appDelegate);
764− tryCompare(appDelegate, "swipeToCloseEnabled", true);
765− }
766−
767− swipeSurfaceUpwards(webbrowserSurfaceId);
768−
769− // webbrowser entry is nowhere to be seen
770− tryCompareFunction(function(){return topSurfaceList.indexForId(webbrowserSurfaceId);}, -1);
771−
772− // nor is its app
773− compare(ApplicationManager.findApplication(webbrowserCheckBox.appId), null);
774−
775− // only unity8-dash surface is left
776− compare(topSurfaceList.count, 1);
777− }
778−
779664 function test_draggingSurfaceKeepsSurfaceFocus() {
780665 var webbrowserSurfaceId = topSurfaceList.nextId;
781666 webbrowserCheckBox.checked = true;
782667 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
783668
784− var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
669+ var appDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
785670 verify(appDelegate);
786671 compare(appDelegate.stage, ApplicationInfoInterface.MainStage);
787672
@@ -789,9 +674,8 @@ Rectangle {
789674
790675 dragToSideStage(webbrowserSurfaceId);
791676
792− var spreadView = findChild(tabletStageLoader, "spreadView")
793− tryCompare(spreadView, "surfaceDragging", false);
794677 tryCompare(appDelegate.surface, "activeFocus", true);
678+ tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
795679 }
796680
797681 function test_dashDoesNotDragToSidestage() {
@@ -799,59 +683,41 @@ Rectangle {
799683 compare(topSurfaceList.applicationAt(0).appId, "unity8-dash");
800684 var dashSurfaceId = topSurfaceList.idAt(0);
801685
802− var appDelegate = findChild(tabletStage, "spreadDelegate_" + dashSurfaceId);
686+ var appDelegate = findChild(stage, "appDelegate_" + dashSurfaceId);
803687 verify(appDelegate);
804688 compare(appDelegate.stage, ApplicationInfoInterface.MainStage);
805689
806− var pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
807− var end_pos = tabletStage.width - sideStage.width / 2;
690+ var pos = stage.width - sideStage.width - (stage.width - sideStage.width) / 2;
691+ var end_pos = stage.width - sideStage.width / 2;
808692
809− multiTouchDragUntil([0,1,2],
810− tabletStage,
811− pos,
812− tabletStage.height / 2,
813− units.gu(3),
814− 0,
693+ multiTouchDragUntil([0,1,2], stage, pos, stage.height / 2, units.gu(3), 0,
815694 function() {
816695 pos += units.gu(3);
817696 return sideStage.shown && !sideStage.showAnimation.running &&
818697 pos >= end_pos;
819698 });
820699
821− var spreadView = findChild(tabletStageLoader, "spreadView")
822− tryCompare(spreadView, "surfaceDragging", false);
700+ waitForRendering(stage);
823701 tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
824702 }
825703
826− function test_switchStageOnRotation() {
704+ function test_switchRestoreStageOnRotation() {
827705 WindowStateStorage.saveStage(webbrowserCheckBox.appId, ApplicationInfoInterface.SideStage)
828706 var webbrowserSurfaceId = topSurfaceList.nextId;
829707 webbrowserCheckBox.checked = true;
830708 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
831709
832− var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
710+ var appDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
833711 verify(appDelegate);
834712 compare(appDelegate.stage, ApplicationInfoInterface.SideStage);
835713
836− tabletStage.shellOrientation = Qt.PortraitOrientation;
837− tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
838− }
839−
840− function test_restoreOriginalStageOnRotation() {
841− var webbrowserSurfaceId = topSurfaceList.nextId;
842− webbrowserCheckBox.checked = true;
843− waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
844−
845− var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
846− verify(appDelegate);
847−
848714 dragToSideStage(webbrowserSurfaceId);
849715
850716 // will be in sidestage now
851− tabletStage.shellOrientation = Qt.PortraitOrientation;
717+ stage.shellOrientation = Qt.PortraitOrientation;
852718 tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
853719
854− tabletStage.shellOrientation = Qt.LandscapeOrientation;
720+ stage.shellOrientation = Qt.LandscapeOrientation;
855721 tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
856722 }
857723
@@ -860,25 +726,25 @@ Rectangle {
860726 webbrowserCheckBox.checked = true;
861727 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
862728
863− var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
729+ var appDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
864730 verify(appDelegate);
865731
866732 dragToSideStage(webbrowserSurfaceId);
867733 // will be in sidestage now
868− tabletStage.shellOrientation = Qt.PortraitOrientation;
734+ stage.shellOrientation = Qt.PortraitOrientation;
869735 tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
870736
871737 webbrowserCheckBox.checked = false;
872738 tryCompare(ApplicationManager, "count", 1);
873739
874740 // back to landscape
875− tabletStage.shellOrientation = Qt.LandscapeOrientation;
741+ stage.shellOrientation = Qt.LandscapeOrientation;
876742
877743 webbrowserSurfaceId = topSurfaceList.nextId;
878744 webbrowserCheckBox.checked = true;
879745 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
880746
881− appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
747+ appDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
882748 verify(appDelegate);
883749 tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
884750 }
tests/qmltests/Stages/tst_WindowResizeArea.qml → tests/qmltests/Stage/tst_WindowResizeArea.qmlrenamed+40−36
@@ -20,7 +20,7 @@ import QtTest 1.0
2020import Unity.Test 0.1
2121import ".."
2222import "../../../qml/Components/PanelState"
23−import "../../../qml/Stages"
23+import "../../../qml/Stage"
2424import Ubuntu.Components 1.3
2525import Ubuntu.Components.ListItems 1.3 as ListItem
2626import Unity.Application 0.1
@@ -34,7 +34,7 @@ Item {
3434 Binding {
3535 target: PanelState
3636 property: "panelHeight"
37− value: units.gu(2)
37+ value: units.gu(3)
3838 }
3939
4040 Component {
@@ -46,12 +46,16 @@ Item {
4646 property alias resizeAreaMinHeight: windowResizeArea.minHeight
4747 x: requestedX
4848 y: requestedY
49− property real requestedX: units.gu(20)
50− property real requestedY: units.gu(20)
49+ property real requestedX: windowedX
50+ property real requestedY: windowedY
5151 width: requestedWidth
5252 height: requestedHeight
53− property real requestedWidth
54− property real requestedHeight
53+ property real requestedWidth: windowedWidth
54+ property real requestedHeight: windowedHeight
55+ property real windowedX: units.gu(20)
56+ property real windowedY: units.gu(20)
57+ property real windowedWidth
58+ property real windowedHeight
5559 property real minimumWidth: 0
5660 property real minimumHeight: 0
5761 property real maximumWidth: 0
@@ -236,10 +240,10 @@ Item {
236240 property var fakeWindow: windowLoader.item
237241
238242 function init() {
239− fakeWindow.requestedX = units.gu(20)
240− fakeWindow.requestedY = units.gu(20)
241− fakeWindow.requestedWidth = units.gu(20)
242− fakeWindow.requestedHeight = units.gu(20)
243+ fakeWindow.windowedX = units.gu(20)
244+ fakeWindow.windowedY = units.gu(20)
245+ fakeWindow.windowedWidth = units.gu(20)
246+ fakeWindow.windowedHeight = units.gu(20)
243247 fakeWindow.resizeAreaMinWidth = units.gu(15);
244248 fakeWindow.resizeAreaMinHeight = units.gu(10);
245249 fakeWindow.minimumWidth = 0;
@@ -264,8 +268,8 @@ Item {
264268 }
265269
266270 function test_resizeWindowRightBottom(data) {
267− var initialWindowX = fakeWindow.requestedX;
268− var initialWindowY = fakeWindow.requestedY;
271+ var initialWindowX = fakeWindow.windowedX;
272+ var initialWindowY = fakeWindow.windowedY;
269273 var initialWindowWidth = fakeWindow.width
270274 var initialWindowHeight = fakeWindow.height
271275
@@ -290,8 +294,8 @@ Item {
290294 }
291295
292296 function test_resizeWindowLeftTop(data) {
293− var initialWindowX = fakeWindow.requestedX;
294− var initialWindowY = fakeWindow.requestedY;
297+ var initialWindowX = fakeWindow.windowedX;
298+ var initialWindowY = fakeWindow.windowedY;
295299 var initialWindowWidth = fakeWindow.width
296300 var initialWindowHeight = fakeWindow.height
297301
@@ -309,8 +313,8 @@ Item {
309313 }
310314
311315 function test_saveRestoreSize() {
312− var initialWindowX = fakeWindow.requestedX;
313− var initialWindowY = fakeWindow.requestedY;
316+ var initialWindowX = fakeWindow.windowedX;
317+ var initialWindowY = fakeWindow.windowedY;
314318 var initialWindowWidth = fakeWindow.width
315319 var initialWindowHeight = fakeWindow.height
316320
@@ -342,8 +346,8 @@ Item {
342346 }
343347
344348 function test_resizeSmallerAndLarger(data) {
345− var initialWindowX = fakeWindow.requestedX;
346− var initialWindowY = fakeWindow.requestedY;
349+ var initialWindowX = fakeWindow.windowedX;
350+ var initialWindowY = fakeWindow.windowedY;
347351 var initialWindowWidth = fakeWindow.width
348352 var initialWindowHeight = fakeWindow.height
349353
@@ -358,18 +362,18 @@ Item {
358362 }
359363
360364 function test_saveRestoreMaximized() {
361− var initialWindowX = fakeWindow.requestedX;
362− var initialWindowY = fakeWindow.requestedY;
365+ var initialWindowX = fakeWindow.windowedX;
366+ var initialWindowY = fakeWindow.windowedY;
363367
364368 var moveDelta = units.gu(5);
365369
366− fakeWindow.requestedX = initialWindowX + moveDelta
367− fakeWindow.requestedY = initialWindowY + moveDelta
370+ fakeWindow.windowedX = initialWindowX + moveDelta
371+ fakeWindow.windowedY = initialWindowY + moveDelta
368372
369373 // Now change the state to maximized. The window should not keep updating the stored values
370374 fakeWindow.maximize()
371− fakeWindow.requestedX = 31415 // 0 is too risky to pass the test even when broken
372− fakeWindow.requestedY = 31415
375+ fakeWindow.windowedX = 31415 // 0 is too risky to pass the test even when broken
376+ fakeWindow.windowedY = 31415
373377
374378 // This will destroy the window and recreate it
375379 windowLoader.active = false;
@@ -399,8 +403,8 @@ Item {
399403 }
400404
401405 function test_restoreMovesIntoBounds(data) {
402− fakeWindow.requestedX = data.x;
403− fakeWindow.requestedY = data.y;
406+ fakeWindow.windowedX = data.x;
407+ fakeWindow.windowedY = data.y;
404408 fakeWindow.width = data.w;
405409 fakeWindow.height = data.h;
406410 waitForRendering(root);
@@ -427,8 +431,8 @@ Item {
427431 and height) increment value.
428432 */
429433 function test_sizeIncrement() {
430− var initialWindowX = fakeWindow.requestedX;
431− var initialWindowY = fakeWindow.requestedY;
434+ var initialWindowX = fakeWindow.windowedX;
435+ var initialWindowY = fakeWindow.windowedY;
432436 var initialWindowWidth = fakeWindow.width
433437 var initialWindowHeight = fakeWindow.height
434438
@@ -458,12 +462,12 @@ Item {
458462 Tests that when dragging a window border you cannot make it bigger than its maximum size
459463 */
460464 function test_maximumSize() {
461− fakeWindow.requestedX = units.gu(1);
462− fakeWindow.requestedY = units.gu(1);
465+ fakeWindow.windowedX = units.gu(1);
466+ fakeWindow.windowedY = units.gu(1);
463467 fakeWindow.resizeAreaMinWidth = 1; // so it does not interfere with anything
464468 fakeWindow.resizeAreaMinHeight = 1; // so it does not interfere with anything
465− fakeWindow.requestedWidth = units.gu(10);
466− fakeWindow.requestedHeight = units.gu(10);
469+ fakeWindow.windowedWidth = units.gu(10);
470+ fakeWindow.windowedHeight = units.gu(10);
467471
468472 fakeWindow.maximumWidth = units.gu(20);
469473 fakeWindow.maximumHeight = units.gu(20);
@@ -488,12 +492,12 @@ Item {
488492 Tests that when dragging a window border you cannot make it smaller than its minimum size
489493 */
490494 function test_minimumSize() {
491− fakeWindow.requestedX = units.gu(1);
492− fakeWindow.requestedY = units.gu(1);
495+ fakeWindow.windowedX = units.gu(1);
496+ fakeWindow.windowedY = units.gu(1);
493497 fakeWindow.resizeAreaMinWidth = 1; // so it does not interfere with anything
494498 fakeWindow.resizeAreaMinHeight = 1; // so it does not interfere with anything
495− fakeWindow.requestedWidth = units.gu(20);
496− fakeWindow.requestedHeight = units.gu(20);
499+ fakeWindow.windowedWidth = units.gu(20);
500+ fakeWindow.windowedHeight = units.gu(20);
497501
498502 fakeWindow.minimumWidth = units.gu(10);
499503 fakeWindow.minimumHeight = units.gu(10);
tests/qmltests/tst_OrientedShell.qml+38−198
@@ -29,7 +29,8 @@ import Utils 0.1
2929
3030import "../../qml"
3131import "../../qml/Components"
32−import "Stages"
32+import "../../qml/Components/PanelState"
33+import "Stage"
3334
3435Rectangle {
3536 id: root
@@ -443,7 +444,7 @@ Rectangle {
443444 SignalSpy { id: signalSpy2 }
444445
445446 Connections {
446− id: spreadRepeaterConnections
447+ id: appRepeaterConnections
447448 ignoreUnknownSignals : true
448449 property var itemAddedCallback: null
449450 onItemAdded: {
@@ -483,8 +484,8 @@ Rectangle {
483484 while (keyboardsModel.count > 0)
484485 MockInputDeviceBackend.removeDevice("/kbd" + (keyboardsModel.count - 1))
485486
486− spreadRepeaterConnections.target = null;
487− spreadRepeaterConnections.itemAddedCallback = null;
487+ appRepeaterConnections.target = null;
488+ appRepeaterConnections.itemAddedCallback = null;
488489 signalSpy.target = null;
489490 signalSpy.signalName = "";
490491
@@ -511,7 +512,7 @@ Rectangle {
511512
512513 var primaryAppWindow = findAppWindowForSurfaceId(primarySurfaceId);
513514 verify(primaryAppWindow)
514− var primaryDelegate = findChild(shell, "spreadDelegate_" + primarySurfaceId);
515+ var primaryDelegate = findChild(shell, "appDelegate_" + primarySurfaceId);
515516
516517 compare(primaryDelegate.focus, true);
517518 compare(primaryApp.rotatesWindowContents, false);
@@ -559,7 +560,7 @@ Rectangle {
559560 compare(ApplicationManager.focusedApplicationId, "primary-oriented-app");
560561 compare(primaryApp.rotatesWindowContents, false);
561562 compare(primaryApp.supportedOrientations, Qt.PrimaryOrientation);
562− var primaryDelegate = findChild(shell, "spreadDelegate_" + primarySurfaceId);
563+ var primaryDelegate = findChild(shell, "appDelegate_" + primarySurfaceId);
563564 compare(primaryDelegate.stage, ApplicationInfoInterface.MainStage);
564565
565566 tryCompareFunction(function(){return primaryApp.surfaceList.count > 0;}, true);
@@ -736,7 +737,7 @@ Rectangle {
736737 compare(musicApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
737738 | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
738739 if (data.deviceName === "manta" || data.deviceName === "flo") {
739− var musicDelegate = findChild(shell, "spreadDelegate_" + musicSurfaceId);
740+ var musicDelegate = findChild(shell, "appDelegate_" + musicSurfaceId);
740741 compare(musicDelegate.stage, ApplicationInfoInterface.MainStage);
741742 }
742743
@@ -873,9 +874,9 @@ Rectangle {
873874 var greeter = findChild(shell, "greeter");
874875 tryCompare(greeter, "animating", false);
875876
876− var twitterDelegate = findChild(shell, "spreadDelegate_" + topLevelSurfaceList.idAt(1));
877+ var twitterDelegate = findChild(shell, "appDelegate_" + topLevelSurfaceList.idAt(1));
877878 compare(twitterDelegate.application.appId, "twitter-webapp");
878− twitterDelegate.clicked();
879+ tap(twitterDelegate, 1, 1);
879880
880881 // now it should finally follow the physical orientation
881882 tryCompare(shell, "transformRotationAngle", 90);
@@ -894,24 +895,10 @@ Rectangle {
894895 WindowStateStorage.saveStage("twitter-webapp", ApplicationInfoInterface.SideStage)
895896 loadShell(data.deviceName);
896897
897− var twitterDelegate = null;
898−
899− verify(spreadRepeaterConnections.target);
900− spreadRepeaterConnections.itemAddedCallback = function(item) {
901− verify(item.application.appId, "twitter-webapp");
902− twitterDelegate = item;
903− signalSpy.target = findInvisibleChild(item, "orientationChangeAnimation");
904− verify(signalSpy.valid);
905− }
906−
907− signalSpy.clear();
908− signalSpy.target = null;
909− signalSpy.signalName = "runningChanged";
910−
911898 var twitterSurfaceId = topLevelSurfaceList.nextId;
912899 var twitterApp = ApplicationManager.startApplication("twitter-webapp");
913900 verify(twitterApp);
914− var twitterDelegate = findChild(shell, "spreadDelegate_" + twitterSurfaceId);
901+ var twitterDelegate = findChild(shell, "appDelegate_" + twitterSurfaceId);
915902 compare(twitterDelegate.stage, ApplicationInfoInterface.SideStage);
916903
917904 // ensure the mock twitter-webapp is as we expect
@@ -919,15 +906,11 @@ Rectangle {
919906 compare(twitterApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
920907 | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
921908
922− // Wait until spreadRepeaterConnections hs caught the new SpreadDelegate and
923− // set up the signalSpy target accordingly.
924− tryCompareFunction(function(){ return signalSpy.target != null && signalSpy.valid; }, true);
925−
926909 tryCompare(twitterDelegate, "orientationChangesEnabled", true);
927910
928− var appWindowWithShadow = findChild(twitterDelegate, "appWindowWithShadow");
929− verify(appWindowWithShadow);
930− tryCompare(appWindowWithShadow, "state", "keepSceneRotation");
911+ var decoratedWindow = findChild(twitterDelegate, "decoratedWindow");
912+ verify(decoratedWindow);
913+ tryCompare(decoratedWindow, "counterRotate", false);
931914
932915 // no reason for any rotation animation to have taken place
933916 compare(signalSpy.count, 0);
@@ -943,8 +926,8 @@ Rectangle {
943926 loadShell(data.deviceName);
944927
945928 var dialerDelegate = null;
946− verify(spreadRepeaterConnections.target);
947− spreadRepeaterConnections.itemAddedCallback = function(item) {
929+ verify(appRepeaterConnections.target);
930+ appRepeaterConnections.itemAddedCallback = function(item) {
948931 dialerDelegate = item;
949932 verify(item.application.appId, "dialer-app");
950933 }
@@ -960,16 +943,12 @@ Rectangle {
960943 tryCompareFunction(function(){ return dialerDelegate != null; }, true);
961944 tryCompare(dialerDelegate, "orientationChangesEnabled", true);
962945
963− var appWindowWithShadow = findChild(dialerDelegate, "appWindowWithShadow");
964− verify(appWindowWithShadow);
965− tryCompare(appWindowWithShadow, "state", "keepSceneRotation");
946+ var decoratedWindow = findChild(dialerDelegate, "decoratedWindow");
947+ verify(decoratedWindow);
948+ tryCompare(decoratedWindow, "counterRotate", false);
966949
967950 // app must have portrait aspect ratio
968− verify(appWindowWithShadow.width < appWindowWithShadow.height);
969−
970− compare(appWindowWithShadow.rotation, 0);
971−
972− verifyAppWindowWithinSpreadDelegateBoundaries(dialerDelegate);
951+ verify(decoratedWindow.width < decoratedWindow.height);
973952
974953 // shell should remain in its primery orientation as the app in the main stage
975954 // is the one that dictates its orientation. In this case it's unity8-dash
@@ -977,83 +956,6 @@ Rectangle {
977956 compare(shell.orientation, orientedShell.orientations.primary);
978957 }
979958
980− function test_sideStageAppsRemainPortraitInSpread_data() {
981− return [
982− {tag: "manta", deviceName: "manta"},
983− {tag: "flo", deviceName: "flo"}
984− ];
985− }
986− function test_sideStageAppsRemainPortraitInSpread(data) {
987− loadShell(data.deviceName);
988−
989−
990− ////
991− // Launch dialer
992− var dialerDelegate = null;
993− verify(spreadRepeaterConnections.target);
994− spreadRepeaterConnections.itemAddedCallback = function(item) {
995− dialerDelegate = item;
996− verify(item.application.appId, "dialer-app");
997− }
998−
999− WindowStateStorage.saveStage("dialer-app", ApplicationInfoInterface.SideStage)
1000− var dialerApp = ApplicationManager.startApplication("dialer-app");
1001− verify(dialerApp);
1002−
1003− // ensure the mock dialer-app is as we expect
1004− compare(dialerApp.rotatesWindowContents, false);
1005− compare(dialerApp.supportedOrientations, Qt.PortraitOrientation | Qt.InvertedPortraitOrientation);
1006−
1007− tryCompareFunction(function(){ return dialerDelegate != null; }, true);
1008− waitUntilAppDelegateIsFullyInit(dialerDelegate);
1009−
1010−
1011− ////
1012− // Launch twitter
1013− var twitterDelegate = null;
1014− spreadRepeaterConnections.itemAddedCallback = function(item) {
1015− twitterDelegate = item;
1016− verify(item.application.appId, "twitter-webapp");
1017− }
1018−
1019− var twitterApp = ApplicationManager.startApplication("twitter-webapp");
1020− verify(twitterApp);
1021− twitterApp.stage = ApplicationInfoInterface.SideStage;
1022−
1023− // ensure the mock twitter-webapp is as we expect
1024− compare(twitterApp.rotatesWindowContents, false);
1025− compare(twitterApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
1026− | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
1027−
1028− tryCompareFunction(function(){ return twitterDelegate != null; }, true);
1029− waitUntilAppDelegateIsFullyInit(twitterDelegate);
1030−
1031− ////
1032− // Edge swipe to show spread and check orientations
1033−
1034− performEdgeSwipeToShowAppSpread();
1035−
1036− {
1037− var appWindowWithShadow = findChild(dialerDelegate, "appWindowWithShadow");
1038− verify(appWindowWithShadow);
1039− tryCompare(appWindowWithShadow, "state", "keepSceneRotation");
1040− compare(appWindowWithShadow.width, dialerDelegate.width);
1041− compare(appWindowWithShadow.height, dialerDelegate.height);
1042− compare(appWindowWithShadow.rotation, 0);
1043− }
1044− {
1045− var appWindowWithShadow = findChild(twitterDelegate, "appWindowWithShadow");
1046− verify(appWindowWithShadow);
1047− tryCompare(appWindowWithShadow, "state", "keepSceneRotation");
1048− compare(appWindowWithShadow.width, twitterDelegate.width);
1049− compare(appWindowWithShadow.height, twitterDelegate.height);
1050− compare(appWindowWithShadow.rotation, 0);
1051− }
1052−
1053− verifyAppWindowWithinSpreadDelegateBoundaries(dialerDelegate);
1054− verifyAppWindowWithinSpreadDelegateBoundaries(twitterDelegate);
1055− }
1056−
1057959 function test_launchedAppHasActiveFocus_data() {
1058960 return [
1059961 {tag: "mako", deviceName: "mako"},
@@ -1449,11 +1351,10 @@ Rectangle {
14491351 function waitUntilAppDelegateIsFullyInit(spreadDelegate) {
14501352 tryCompare(spreadDelegate, "orientationChangesEnabled", true);
14511353
1452− var appWindowWithShadow = findChild(spreadDelegate, "appWindowWithShadow");
1453− verify(appWindowWithShadow);
1454− tryCompare(appWindowWithShadow, "state", "keepSceneRotation");
1354+ var decoratedWindow = findChild(spreadDelegate, "decoratedWindow");
1355+ tryCompare(decoratedWindow, "counterRotate", false);
14551356
1456− var appWindowStates = findInvisibleChild(appWindowWithShadow, "applicationWindowStateGroup");
1357+ var appWindowStates = findInvisibleChild(decoratedWindow, "applicationWindowStateGroup");
14571358 verify(appWindowStates);
14581359 tryCompare(appWindowStates, "state", "surface");
14591360 }
@@ -1491,8 +1392,7 @@ Rectangle {
14911392 }
14921393
14931394 function performLeftEdgeSwipeToSwitchToDash() {
1494− var spreadView = findChild(shell, "spreadView");
1495− var swipeLength = spreadView.width * 0.7;
1395+ var swipeLength = shell.width * 0.7;
14961396
14971397 var touchStartX = 1;
14981398 var touchStartY = shell.height / 2;
@@ -1507,35 +1407,13 @@ Rectangle {
15071407 // swipe just enough to ensure an app switch action.
15081408 // If we swipe too much we will trigger the spread mode
15091409 // and we don't want that.
1510− var spreadView = findChild(shell, "spreadView");
1511− verify(spreadView);
1512−
15131410 verify(topLevelSurfaceList.count >= 2);
15141411 var previousSurfaceId = topLevelSurfaceList.idAt(1);
15151412
15161413 var touchStartX = shell.width - 1;
15171414 var touchStartY = shell.height / 2;
15181415
1519− var condition;
1520− if (applicationArguments.deviceName === "phone") {
1521− condition = function() {
1522− return spreadView.shiftedContentX > units.gu(2) &&
1523− spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width;
1524− };
1525− } else {
1526− condition = function() {
1527− return spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker1
1528− && spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2;
1529− };
1530− }
1531−
1532− touchDragUntil(shell,
1533− touchStartX, touchStartY,
1534− -units.gu(1), 0,
1535− condition);
1536−
1537− // ensure the app switch animation has ended
1538− tryCompare(spreadView, "shiftedContentX", 0);
1416+ touchFlick(shell, touchStartX, touchStartY, touchStartX - units.gu(8), touchStartY, units.gu(4), 10);
15391417
15401418 tryCompareFunction(function(){ return topLevelSurfaceList.idAt(0); }, previousSurfaceId);
15411419 }
@@ -1546,11 +1424,9 @@ Rectangle {
15461424 shell.width - 1, touchStartY,
15471425 0, touchStartY);
15481426
1549− var spreadView = findChild(shell, "spreadView");
1550− verify(spreadView);
1551− tryCompare(spreadView, "phase", 2);
1552− tryCompare(spreadView, "flicking", false);
1553− tryCompare(spreadView, "moving", false);
1427+ var stage = findChild(shell, "stage");
1428+ tryCompare(stage, "state", "spread");
1429+ waitForRendering(stage);
15541430 }
15551431
15561432 function showPowerDialog() {
@@ -1559,19 +1435,6 @@ Rectangle {
15591435 dialogsPrivate.showPowerDialog();
15601436 }
15611437
1562− function verifyAppWindowWithinSpreadDelegateBoundaries(spreadDelegate) {
1563− var appWindowWithShadow = findChild(spreadDelegate, "appWindowWithShadow");
1564− verify(appWindowWithShadow);
1565−
1566− var windowInDelegateCoords = appWindowWithShadow.mapToItem(spreadDelegate, 0, 0,
1567− appWindowWithShadow.width, appWindowWithShadow.height);
1568−
1569− compare(windowInDelegateCoords.x, 0);
1570− compare(windowInDelegateCoords.y, 0);
1571− compare(windowInDelegateCoords.width, spreadDelegate.width);
1572− compare(windowInDelegateCoords.height, spreadDelegate.height);
1573− }
1574−
15751438 function swipeAwayGreeter() {
15761439 var greeter = findChild(shell, "greeter");
15771440 tryCompare(greeter, "fullyShown", true);
@@ -1606,9 +1469,6 @@ Rectangle {
16061469 topLevelSurfaceList = findInvisibleChild(shell, "topLevelSurfaceList");
16071470 verify(topLevelSurfaceList);
16081471
1609− var stageLoader = findChild(shell, "applicationsDisplayLoader");
1610− verify(stageLoader);
1611−
16121472 tryCompare(shell, "waitingOnGreeter", false); // reset by greeter when ready
16131473
16141474 waitUntilShellIsInOrientation(root.physicalOrientation0);
@@ -1617,9 +1477,9 @@ Rectangle {
16171477
16181478 swipeAwayGreeter();
16191479
1620− var spreadRepeater = findChild(stageLoader, "spreadRepeater");
1621− if (spreadRepeater) {
1622− spreadRepeaterConnections.target = spreadRepeater;
1480+ var appRepeater = findChild(shell, "appRepeater");
1481+ if (appRepeater) {
1482+ appRepeaterConnections.target = appRepeater;
16231483 }
16241484 }
16251485
@@ -1644,15 +1504,6 @@ Rectangle {
16441504 return false;
16451505 }
16461506
1647− var topMargin = 0.;
1648− if (!app.fullscreen) {
1649− var appsDisplayLoader = findChild(shell, "applicationsDisplayLoader");
1650− verify(appsDisplayLoader);
1651− verify(appsDisplayLoader.item);
1652− verify(appsDisplayLoader.item.maximizedAppTopMargin !== undefined);
1653− topMargin = appsDisplayLoader.item.maximizedAppTopMargin;
1654− }
1655−
16561507 var surfaceItem = findSurfaceItem(item, surface);
16571508 if (!surfaceItem) {
16581509 console.warn("no surfaceItem rendering app surface");
@@ -1660,15 +1511,16 @@ Rectangle {
16601511 }
16611512 var point = surfaceItem.mapToItem(orientedShell, 0, 0);
16621513
1514+ print("exptectedAngle", expectedAngle, point.x, point.y)
16631515 switch (expectedAngle) {
16641516 case 0:
1665− return point.x === 0. && point.y === topMargin;
1517+ return point.x === 0 && point.y === PanelState.panelHeight;
16661518 case 90:
1667− return point.x === orientedShell.width - topMargin && point.y === 0;
1519+ return point.x === orientedShell.width - PanelState.panelHeight && point.y === 0;
16681520 case 180:
1669− return point.x === orientedShell.width && point.y === orientedShell.height - topMargin;
1521+ return point.x === orientedShell.width && point.y === orientedShell.height - PanelState.panelHeight;
16701522 default: // 270
1671− return point.x === topMargin && point.y === orientedShell.height;
1523+ return point.x === PanelState.panelHeight && point.y === orientedShell.height;
16721524 }
16731525 }
16741526
@@ -1690,24 +1542,12 @@ Rectangle {
16901542 }
16911543
16921544 function swipeToCloseCurrentAppInSpread() {
1693− var spreadView = findChild(shell, "spreadView");
1694− verify(spreadView);
1695−
1696− var delegateToClose = findChild(spreadView, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
1545+ var delegateToClose = findChild(shell, "appDelegate_" + topLevelSurfaceList.idAt(0));
16971546 verify(delegateToClose);
16981547
1699− var appIdToClose = ApplicationManager.get(0).appId;;
1548+ var appIdToClose = ApplicationManager.get(0).appId;
17001549 var appCountBefore = ApplicationManager.count;
17011550
1702− // ensure the current app is widely visible by swiping to the right,
1703− // which will move the app windows accordingly
1704− touchFlick(shell,
1705− shell.width * 0.25, shell.width / 2,
1706− shell.width, shell.width / 2);
1707−
1708− tryCompare(spreadView, "flicking", false);
1709− tryCompare(spreadView, "moving", false);
1710−
17111551 // Swipe up close to its left edge, as it is the only area of it guaranteed to be exposed
17121552 // in the spread. Eg: its center could be covered by some other delegate.
17131553 touchFlick(delegateToClose,
tests/qmltests/tst_Shell.qml+182−163
@@ -36,7 +36,7 @@ import Utils 0.1
3636import "../../qml"
3737import "../../qml/Components"
3838import "../../qml/Components/PanelState"
39−import "Stages"
39+import "Stage"
4040
4141Rectangle {
4242 id: root
@@ -51,6 +51,8 @@ Rectangle {
5151 shellLoader.active = true;
5252 }
5353
54+ property var shell: shellLoader.item ? shellLoader.item : null
55+
5456 Item {
5557 id: shellContainer
5658 anchors.left: root.left
@@ -108,6 +110,7 @@ Rectangle {
108110 sourceComponent: Component {
109111 Shell {
110112 id: __shell
113+ objectName: "shell"
111114 usageScenario: usageScenarioSelector.model[usageScenarioSelector.selectedIndex]
112115 nativeWidth: width
113116 nativeHeight: height
@@ -184,22 +187,44 @@ Rectangle {
184187 callManager.foregroundCall = callManager.foregroundCall ? null : phoneCall;
185188 }
186189 }
187− }
188− Button {
189− text: "Show Launcher"
190− activeFocusOnPress: false
191− onClicked: {
192− if (shellLoader.status !== Loader.Ready)
193− return;
190+ Button {
191+ text: "Show Launcher"
192+ activeFocusOnPress: false
193+ onClicked: {
194+ if (shellLoader.status !== Loader.Ready)
195+ return;
194196
195− var launcher = testCase.findChild(shellLoader.item, "launcher");
196− launcher.state = "visible";
197+ var launcher = testCase.findChild(shellLoader.item, "launcher");
198+ launcher.state = "visible";
199+ }
200+ }
201+ Button {
202+ text: "Print focused"
203+ activeFocusOnPress: false
204+ onClicked: {
205+ var childs = new Array(0);
206+ childs.push(shellLoader.item)
207+ while (childs.length > 0) {
208+ if (childs[0].activeFocus && childs[0].focus && childs[0].objectName != "shell") {
209+ console.log("Active focus is on item:", childs[0]);
210+ return;
211+ }
212+ for (var i in childs[0].children) {
213+ childs.push(childs[0].children[i])
214+ }
215+ childs.splice(0, 1);
216+ }
217+ console.log("No active focused item found within shell.")
218+ }
197219 }
198220 }
221+ Label {
222+ text: "LightDM mock mode"
223+ }
224+
199225 ListItem.ItemSelector {
200226 anchors { left: parent.left; right: parent.right }
201227 activeFocusOnPress: false
202− text: "LightDM mock mode"
203228 model: ["single", "single-passphrase", "single-pin", "full"]
204229 onSelectedIndexChanged: {
205230 shellLoader.active = false;
@@ -208,33 +233,41 @@ Rectangle {
208233 shellLoader.active = true;
209234 }
210235 }
236+ Label {
237+ text: "Size"
238+ }
239+
211240 ListItem.ItemSelector {
241+ id: sizeSelector
212242 anchors { left: parent.left; right: parent.right }
213243 activeFocusOnPress: false
214− text: "Size"
215244 model: ["phone", "tablet", "desktop"]
216245 onSelectedIndexChanged: {
217− shellLoader.active = false;
218246 shellLoader.state = model[selectedIndex];
219− shellLoader.active = true;
220247 }
221248 }
249+ Label {
250+ text: "Usage scenario"
251+ }
252+
222253 ListItem.ItemSelector {
223254 id: usageScenarioSelector
224255 anchors { left: parent.left; right: parent.right }
225256 activeFocusOnPress: false
226− text: "Usage scenario"
227257 model: ["phone", "tablet", "desktop"]
228258 }
229259 MouseTouchEmulationCheckbox {
230260 id: mouseEmulation
231261 checked: true
232262 }
263+ Label {
264+ text: "Ctrl key as"
265+ }
266+
233267 ListItem.ItemSelector {
234268 id: ctrlModifier
235269 anchors { left: parent.left; right: parent.right }
236270 activeFocusOnPress: false
237− text: "Ctrl key as"
238271 model: ["Ctrl", "Alt", "Super"]
239272 onSelectedIndexChanged: {
240273 var keyMapper = testCase.findChild(shellContainer, "physicalKeysMapper");
@@ -261,6 +294,7 @@ Rectangle {
261294 Button {
262295 text: "Start all apps"
263296 width: parent.width
297+ activeFocusOnPress: false
264298 onClicked: {
265299 for (var i = 0; i < ApplicationManager.availableApplications.length; i++) {
266300 var appId = ApplicationManager.availableApplications[i];
@@ -270,7 +304,7 @@ Rectangle {
270304 }
271305
272306 Repeater {
273− id: appRepeater
307+ id: appCheckBoxRepeater
274308 model: ApplicationManager.availableApplications
275309 ApplicationCheckBox {
276310 appId: modelData
@@ -571,6 +605,38 @@ Rectangle {
571605 compare(ApplicationManager.get(0).appId, "unity8-dash");
572606 }
573607
608+ function test_phoneLeftEdgeDrag_data() {
609+ return [
610+ {tag: "without launcher",
611+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
612+ launcherHides: true, greeterShown: false},
613+
614+ {tag: "with launcher",
615+ revealLauncher: true, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
616+ launcherHides: true, greeterShown: false},
617+
618+ {tag: "small swipe",
619+ revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
620+ launcherHides: false, greeterShown: false},
621+
622+ {tag: "long swipe",
623+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
624+ launcherHides: true, greeterShown: false},
625+
626+ {tag: "small swipe with greeter",
627+ revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
628+ launcherHides: false, greeterShown: true},
629+
630+ {tag: "long swipe with greeter",
631+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
632+ launcherHides: true, greeterShown: true},
633+
634+ {tag: "swipe over dash",
635+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "unity8-dash",
636+ launcherHides: false, greeterShown: false},
637+ ];
638+ }
639+
574640 function test_snapDecisionDismissalReturnsFocus() {
575641 loadShell("phone");
576642 swipeAwayGreeter();
@@ -635,38 +701,6 @@ Rectangle {
635701 mockNotificationsModel.append(n)
636702 }
637703
638− function test_phoneLeftEdgeDrag_data() {
639− return [
640− {tag: "without launcher",
641− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
642− launcherHides: true, greeterShown: false},
643−
644− {tag: "with launcher",
645− revealLauncher: true, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
646− launcherHides: true, greeterShown: false},
647−
648− {tag: "small swipe",
649− revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
650− launcherHides: false, greeterShown: false},
651−
652− {tag: "long swipe",
653− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
654− launcherHides: true, greeterShown: false},
655−
656− {tag: "small swipe with greeter",
657− revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
658− launcherHides: false, greeterShown: true},
659−
660− {tag: "long swipe with greeter",
661− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
662− launcherHides: true, greeterShown: true},
663−
664− {tag: "swipe over dash",
665− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "unity8-dash",
666− launcherHides: false, greeterShown: false},
667− ];
668− }
669−
670704 function test_phoneLeftEdgeDrag(data) {
671705 loadShell("phone");
672706 swipeAwayGreeter();
@@ -1365,7 +1399,7 @@ Rectangle {
13651399 function test_tapOnRightEdgeReachesApplicationSurface() {
13661400 loadShell("phone");
13671401 swipeAwayGreeter();
1368− var topmostSpreadDelegate = findChild(shell, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
1402+ var topmostSpreadDelegate = findChild(shell, "appDelegate_" + topLevelSurfaceList.idAt(0));
13691403 verify(topmostSpreadDelegate);
13701404
13711405 waitUntilFocusedApplicationIsShowingItsSurface();
@@ -1373,7 +1407,7 @@ Rectangle {
13731407 var topmostSurfaceItem = findChild(topmostSpreadDelegate, "surfaceItem");
13741408 verify(topmostSurfaceItem);
13751409
1376− var rightEdgeDragArea = findChild(shell, "spreadDragArea");
1410+ var rightEdgeDragArea = findChild(shell, "rightEdgeDragArea");
13771411 topmostSurfaceItem.touchPressCount = 0;
13781412 topmostSurfaceItem.touchReleaseCount = 0;
13791413
@@ -1394,9 +1428,9 @@ Rectangle {
13941428 function test_rightEdgeDragDoesNotReachApplicationSurface() {
13951429 loadShell("phone");
13961430 swipeAwayGreeter();
1397− var topmostSpreadDelegate = findChild(shell, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
1431+ var topmostSpreadDelegate = findChild(shell, "appDelegate_" + topLevelSurfaceList.idAt(0));
13981432 var topmostSurfaceItem = findChild(topmostSpreadDelegate, "surfaceItem");
1399− var rightEdgeDragArea = findChild(shell, "spreadDragArea");
1433+ var rightEdgeDragArea = findChild(shell, "rightEdgeDragArea");
14001434
14011435 topmostSurfaceItem.touchPressCount = 0;
14021436 topmostSurfaceItem.touchReleaseCount = 0;
@@ -1414,7 +1448,7 @@ Rectangle {
14141448
14151449 function waitUntilFocusedApplicationIsShowingItsSurface()
14161450 {
1417− var spreadDelegate = findChild(shell, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
1451+ var spreadDelegate = findChild(shell, "appDelegate_" + topLevelSurfaceList.idAt(0));
14181452 var appState = findInvisibleChild(spreadDelegate, "applicationWindowStateGroup");
14191453 tryCompare(appState, "state", "surface");
14201454 var transitions = appState.transitions;
@@ -1433,8 +1467,7 @@ Rectangle {
14331467
14341468 // check if it's indeed showing the spread
14351469 var stage = findChild(shell, "stage");
1436− var spreadView = findChild(stage, "spreadView");
1437− tryCompare(spreadView, "phase", 2);
1470+ tryCompare(stage, "state", "spread");
14381471 }
14391472
14401473 function test_tapUbuntuIconInLauncherOverAppSpread() {
@@ -1470,8 +1503,7 @@ Rectangle {
14701503 // check that the stage has left spread mode.
14711504 {
14721505 var stage = findChild(shell, "stage");
1473− var spreadView = findChild(stage, "spreadView");
1474− tryCompare(spreadView, "phase", 0);
1506+ tryCompare(stage, "state", "staged");
14751507 }
14761508
14771509 // check that the launcher got dismissed
@@ -1538,21 +1570,6 @@ Rectangle {
15381570 }
15391571 }
15401572
1541− function test_stageLoader_data() {
1542− return [
1543− {tag: "phone", source: "Stages/PhoneStage.qml", formFactor: "phone", usageScenario: "phone"},
1544− {tag: "tablet", source: "Stages/TabletStage.qml", formFactor: "tablet", usageScenario: "tablet"},
1545− {tag: "desktop", source: "Stages/DesktopStage.qml", formFactor: "tablet", usageScenario: "desktop"}
1546− ]
1547− }
1548−
1549− function test_stageLoader(data) {
1550− loadShell(data.formFactor);
1551− shell.usageScenario = data.usageScenario;
1552− var stageLoader = findChild(shell, "applicationsDisplayLoader");
1553− verify(String(stageLoader.source).indexOf(data.source) >= 0);
1554− }
1555−
15561573 function test_launcherInverted_data() {
15571574 return [
15581575 {tag: "phone", formFactor: "phone", usageScenario: "phone", launcherInverted: true},
@@ -1666,47 +1683,41 @@ Rectangle {
16661683 function test_altTabWrapAround() {
16671684 loadDesktopShellWithApps();
16681685
1669− var desktopStage = findChild(shell, "stage");
1670− verify(desktopStage !== null)
1671−
1672− var desktopSpread = findChild(shell, "spread");
1673− verify(desktopSpread !== null)
1674−
1675− var spreadContainer = findInvisibleChild(shell, "spreadContainer")
1676− verify(spreadContainer !== null)
1686+ var stage = findChild(shell, "stage");
1687+ verify(stage !== null)
16771688
1678− var spreadRepeater = findInvisibleChild(shell, "spreadRepeater")
1679− verify(spreadRepeater !== null)
1689+ var spread = findChild(shell, "spreadItem");
1690+ verify(spread !== null)
16801691
16811692 // remember the focused appId
16821693 var focused = ApplicationManager.get(ApplicationManager.findApplication(ApplicationManager.focusedApplicationId));
16831694
1684− tryCompare(desktopSpread, "state", "")
1695+ tryCompare(stage, "state", "windowed")
16851696
16861697 // Just press Alt, make sure the spread comes up
16871698 keyPress(Qt.Key_Alt);
16881699 keyClick(Qt.Key_Tab);
1689− tryCompare(desktopSpread, "state", "altTab")
1690− tryCompare(spreadRepeater, "highlightedIndex", 1)
1700+ tryCompare(stage, "state", "spread")
1701+ tryCompare(spread, "highlightedIndex", 1)
16911702 waitForRendering(shell)
16921703
16931704 // Now press and hold Tab, make sure the highlight moves all the way but stops at the last one
16941705 // We can't simulate a pressed key with keyPress() currently, so let's inject the events
16951706 // at API level. Jump for 10 times, verify that it's still at the last one and didn't wrap around.
16961707 for (var i = 0; i < 10; i++) {
1697− desktopSpread.selectNext(true); // true == isAutoRepeat
1708+ spread.selectNext(true); // true == isAutoRepeat
16981709 wait(0); // Trigger the event loop to make sure all the things happen
16991710 }
1700− tryCompare(spreadRepeater, "highlightedIndex", 6)
1711+ tryCompare(spread, "highlightedIndex", 6)
17011712
17021713 // Now release it once, and verify that it does wrap around with an additional Tab press
17031714 keyRelease(Qt.Key_Tab);
17041715 keyClick(Qt.Key_Tab);
1705− tryCompare(spreadRepeater, "highlightedIndex", 0)
1716+ tryCompare(spread, "highlightedIndex", 0)
17061717
17071718 // Release control, check if spread disappears
17081719 keyRelease(Qt.Key_Alt)
1709− tryCompare(desktopSpread, "state", "")
1720+ tryCompare(stage, "state", "windowed")
17101721
17111722 // Make sure that after wrapping around once, we have the same one focused as at the beginning
17121723 var focusedAppSurface = focused.surfaceList.get(0);
@@ -1717,30 +1728,30 @@ Rectangle {
17171728 function test_altBackTabNavigation() {
17181729 loadDesktopShellWithApps();
17191730
1720− var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1721− verify(spreadRepeater !== null);
1731+ var spreadItem = findChild(shell, "spreadItem");
1732+ verify(spreadItem !== null);
17221733
17231734 keyPress(Qt.Key_Alt)
17241735 keyClick(Qt.Key_Tab);
1725− tryCompare(spreadRepeater, "highlightedIndex", 1);
1736+ tryCompare(spreadItem, "highlightedIndex", 1);
17261737
17271738 keyClick(Qt.Key_Tab);
1728− tryCompare(spreadRepeater, "highlightedIndex", 2);
1739+ tryCompare(spreadItem, "highlightedIndex", 2);
17291740
17301741 keyClick(Qt.Key_Tab);
1731− tryCompare(spreadRepeater, "highlightedIndex", 3);
1742+ tryCompare(spreadItem, "highlightedIndex", 3);
17321743
17331744 keyClick(Qt.Key_Tab);
1734− tryCompare(spreadRepeater, "highlightedIndex", 4);
1745+ tryCompare(spreadItem, "highlightedIndex", 4);
17351746
17361747 keyClick(Qt.Key_Backtab);
1737− tryCompare(spreadRepeater, "highlightedIndex", 3);
1748+ tryCompare(spreadItem, "highlightedIndex", 3);
17381749
17391750 keyClick(Qt.Key_Backtab);
1740− tryCompare(spreadRepeater, "highlightedIndex", 2);
1751+ tryCompare(spreadItem, "highlightedIndex", 2);
17411752
17421753 keyClick(Qt.Key_Backtab);
1743− tryCompare(spreadRepeater, "highlightedIndex", 1);
1754+ tryCompare(spreadItem, "highlightedIndex", 1);
17441755
17451756 keyRelease(Qt.Key_Alt);
17461757 }
@@ -1776,21 +1787,24 @@ Rectangle {
17761787 function test_closeFromSpread() {
17771788 loadDesktopShellWithApps()
17781789
1779− var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1780− verify(spreadRepeater !== null);
1790+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1791+ verify(appRepeater !== null);
1792+
1793+ var spreadItem = findChild(shell, "spreadItem");
1794+ verify(spreadItem !== null);
17811795
17821796 keyPress(Qt.Key_Alt)
17831797 keyClick(Qt.Key_Tab);
17841798
17851799 var surfaceId = topLevelSurfaceList.idAt(2);
1786− var spreadDelegate2 = spreadRepeater.itemAt(2);
1800+ var spreadDelegate2 = appRepeater.itemAt(2);
17871801 var closeMouseArea = findChild(spreadDelegate2, "closeMouseArea");
17881802
17891803 // Move the mosue over tile 2 and verify the close button becomes visible
17901804 var x = 0;
17911805 var y = shell.height * .5;
17921806 mouseMove(shell, x, y)
1793− while (spreadRepeater.highlightedIndex !== 2 && x <= 4000) {
1807+ while (spreadItem.highlightedIndex !== 2 && x <= 4000) {
17941808 x+=10;
17951809 mouseMove(shell, x, y)
17961810 wait(0); // spin the loop so bindings get evaluated
@@ -1820,37 +1834,37 @@ Rectangle {
18201834 loadDesktopShellWithApps()
18211835
18221836 var stage = findChild(shell, "stage");
1823− var spread = findChild(stage, "spread");
1824− waitForRendering(spread)
1837+ var spreadItem = findChild(stage, "spreadItem");
1838+// waitForRendering(spread)
18251839
1826− var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1827− verify(spreadRepeater !== null);
1840+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1841+ verify(appRepeater !== null);
18281842
18291843 keyPress(Qt.Key_Alt)
18301844 keyClick(Qt.Key_Tab);
18311845
18321846 var surface = topLevelSurfaceList.surfaceAt(2);
1833− var spreadDelegate2 = spreadRepeater.itemAt(2);
1834− var clippedSpreadDelegate = findChild(spreadDelegate2, "clippedSpreadDelegate");
1847+ var spreadDelegate2 = appRepeater.itemAt(2);
1848+ var decoratedWindow = findChild(spreadDelegate2, "decoratedWindow");
18351849
1836− tryCompare(spread, "state", "altTab");
1850+ tryCompare(stage, "state", "spread");
18371851
18381852 // Move the mouse over tile 2 and verify the highlight becomes visible
18391853 var x = 0;
1840− var y = shell.height * (data.tileInfo ? .95 : 0.5)
1854+ var y = shell.height * (data.tileInfo ? .9 : 0.5)
18411855 mouseMove(shell, x, y)
1842− while (spreadRepeater.highlightedIndex !== 2 && x <= 4000) {
1856+ while (spreadItem.highlightedIndex !== 2 && x <= 4000) {
18431857 x+=10;
18441858 mouseMove(shell, x, y)
18451859 wait(0); // spin the loop so bindings get evaluated
18461860 }
1847− tryCompare(clippedSpreadDelegate, "highlightShown", true);
1861+ tryCompare(decoratedWindow, "showHighlight", true);
18481862
18491863 // Click the tile
1850− mouseClick(clippedSpreadDelegate, clippedSpreadDelegate.width / 2, clippedSpreadDelegate.height / 2)
1864+ mouseClick(decoratedWindow, units.gu(2), decoratedWindow.height / 2)
18511865
18521866 // Verify that we left the spread and app2 is the focused one now
1853− tryCompare(stage, "state", "");
1867+ tryCompare(stage, "state", "windowed");
18541868 tryCompare(surface, "focused", true);
18551869
18561870 keyRelease(Qt.Key_Alt);
@@ -1912,41 +1926,6 @@ Rectangle {
19121926 tryCompare(hoverMouseArea, "visible", false)
19131927 }
19141928
1915− function test_workspacePreviewsHighlightedApp() {
1916− loadDesktopShellWithApps()
1917−
1918− var targetZ = topLevelSurfaceList.count + 1;
1919−
1920− var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1921− verify(spreadRepeater !== null);
1922−
1923− var appRepeater = findInvisibleChild(shell, "appRepeater");
1924− verify(appRepeater !== null);
1925−
1926− keyPress(Qt.Key_Alt)
1927− keyClick(Qt.Key_Tab);
1928−
1929− tryCompare(spreadRepeater, "highlightedIndex", 1);
1930− tryCompare(appRepeater.itemAt(1), "z", targetZ)
1931−
1932− var x = 0;
1933− var y = shell.height * .75;
1934− mouseMove(shell, x, y)
1935−
1936− for (var i = 0; i < 7; i++) {
1937− while (spreadRepeater.highlightedIndex != i && x <= 4000) {
1938− tryCompare(appRepeater.itemAt(spreadRepeater.highlightedIndex), "z", targetZ)
1939− x+=10;
1940− mouseMove(shell, x, y)
1941− wait(0); // spin the loop so bindings get evaluated
1942− }
1943− }
1944−
1945− verify(y < 4000);
1946−
1947− keyRelease(Qt.Key_Alt);
1948− }
1949−
19501929 function test_focusAppFromLauncherExitsSpread_data() {
19511930 return [
19521931 {tag: "autohide launcher", launcherLocked: false },
@@ -1957,7 +1936,7 @@ Rectangle {
19571936 function test_focusAppFromLauncherExitsSpread(data) {
19581937 loadDesktopShellWithApps()
19591938 var launcher = findChild(shell, "launcher");
1960− var desktopSpread = findChild(shell, "spread");
1939+ var stage = findChild(shell, "stage");
19611940 var bfb = findChild(launcher, "buttonShowDashHome");
19621941
19631942 GSettingsController.setAutohideLauncher(!data.launcherLocked);
@@ -1966,7 +1945,7 @@ Rectangle {
19661945 keyPress(Qt.Key_Alt)
19671946 keyClick(Qt.Key_Tab);
19681947
1969− tryCompare(desktopSpread, "state", "altTab")
1948+ tryCompare(stage, "state", "spread")
19701949
19711950 if (!data.launcherLocked) {
19721951 revealLauncherByEdgePushWithMouse();
@@ -1979,7 +1958,7 @@ Rectangle {
19791958 if (!data.launcherLocked) {
19801959 tryCompare(launcher, "state", "")
19811960 }
1982− tryCompare(desktopSpread, "state", "")
1961+ tryCompare(stage, "state", "windowed")
19831962
19841963 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash")
19851964
@@ -2147,10 +2126,10 @@ Rectangle {
21472126
21482127 // Sanity checking
21492128 if (data.usageScenario === "tablet") {
2150− var app1Delegate = findChild(shell, "spreadDelegate_" + app1SurfaceId);
2129+ var app1Delegate = findChild(shell, "appDelegate_" + app1SurfaceId);
21512130 compare(app1Delegate.stage, ApplicationInfoInterface.MainStage);
21522131
2153− var app2Delegate = findChild(shell, "spreadDelegate_" + app2SurfaceId);
2132+ var app2Delegate = findChild(shell, "appDelegate_" + app2SurfaceId);
21542133 compare(app2Delegate.stage, ApplicationInfoInterface.MainStage);
21552134 }
21562135 verify(!app1.isTouchApp);
@@ -2188,10 +2167,10 @@ Rectangle {
21882167
21892168 // Sanity checking
21902169 if (data.usageScenario === "tablet") {
2191− var app1Delegate = findChild(shell, "spreadDelegate_" + app1SurfaceId);
2170+ var app1Delegate = findChild(shell, "appDelegate_" + app1SurfaceId);
21922171 compare(app1Delegate.stage, ApplicationInfoInterface.MainStage);
21932172
2194− var app2Delegate = findChild(shell, "spreadDelegate_" + app2SurfaceId);
2173+ var app2Delegate = findChild(shell, "appDelegate_" + app2SurfaceId);
21952174 compare(app2Delegate.stage, ApplicationInfoInterface.MainStage);
21962175 }
21972176
@@ -2278,19 +2257,19 @@ Rectangle {
22782257 function test_superTabToCycleLauncher(data) {
22792258 loadShell("desktop");
22802259 shell.usageScenario = "desktop";
2260+ waitForRendering(shell);
22812261 GSettingsController.setAutohideLauncher(!data.launcherLocked);
22822262 waitForRendering(shell);
22832263
2264+ var stage = findChild(shell, "stage");
22842265 var launcher = findChild(shell, "launcher");
22852266 var launcherPanel = findChild(launcher, "launcherPanel");
22862267 var firstAppInLauncher = LauncherModel.get(0).appId;
2287−
22882268 compare(launcher.state, data.launcherLocked ? "visible": "");
22892269 compare(launcherPanel.highlightIndex, -2);
22902270 compare(ApplicationManager.focusedApplicationId, "unity8-dash");
22912271
22922272 // Use Super + Tab Tab to cycle to the first entry in the launcher
2293− wait(1000);
22942273 keyPress(Qt.Key_Super_L, Qt.MetaModifier);
22952274 keyClick(Qt.Key_Tab);
22962275 tryCompare(launcher, "state", "visible");
@@ -2311,6 +2290,7 @@ Rectangle {
23112290 tryCompare(launcher, "state", data.launcherLocked ? "visible" : "");
23122291 tryCompare(launcherPanel, "highlightIndex", -2);
23132292 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
2293+ tryCompare(stage, "focus", true)
23142294 }
23152295
23162296 function test_longpressSuperOpensLauncherAndShortcutsOverlay() {
@@ -2371,6 +2351,7 @@ Rectangle {
23712351 waitForRendering(shell);
23722352 var appContainer = findChild(shell, "appContainer");
23732353 var launcher = findChild(shell, "launcher");
2354+ var launcherPanel = findChild(launcher, "launcherPanel");
23742355
23752356 var appSurfaceId = topLevelSurfaceList.nextId;
23762357 var app = ApplicationManager.startApplication("music-app");
@@ -2381,10 +2362,12 @@ Rectangle {
23812362 waitForRendering(shell);
23822363
23832364 GSettingsController.setAutohideLauncher(true);
2365+ tryCompare(launcherPanel, "x", -launcher.panelWidth)
23842366 waitForRendering(shell)
23852367 var hiddenSize = appDelegate.width;
23862368
23872369 GSettingsController.setAutohideLauncher(false);
2370+ tryCompare(launcherPanel, "x", 0)
23882371 waitForRendering(shell)
23892372 var shownSize = appDelegate.width;
23902373
@@ -2422,9 +2405,9 @@ Rectangle {
24222405
24232406 function test_inputEventsOnEdgesEndUpInAppSurface_data() {
24242407 return [
2425− { tag: "phone", repeaterName: "spreadRepeater" },
2426− { tag: "tablet", repeaterName: "spreadRepeater" },
2427− { tag: "desktop", repeaterName: "appRepeater" },
2408+ { tag: "phone" },
2409+ { tag: "tablet" },
2410+ { tag: "desktop" },
24282411 ]
24292412 }
24302413
@@ -2439,7 +2422,7 @@ Rectangle {
24392422 var app = ApplicationManager.startApplication("camera-app");
24402423 waitUntilAppWindowIsFullyLoaded(appSurfaceId);
24412424
2442− var appRepeater = findChild(shell, data.repeaterName);
2425+ var appRepeater = findChild(shell, "appRepeater");
24432426 var topmostAppDelegate = appRepeater.itemAt(0);
24442427 verify(topmostAppDelegate);
24452428
@@ -2714,5 +2697,41 @@ Rectangle {
27142697 tryCompare(topLevelSurfaceList, "count", countBeforeClose - 1);
27152698 tryCompareFunction(function() { return ApplicationManager.focusedApplicationId; }, "calendar-app");
27162699 }
2700+
2701+ function test_oskDisplacesWindow_data() {
2702+ return [
2703+ {tag: "no need to displace", windowHeight: units.gu(10), windowY: units.gu(5), targetDisplacement: units.gu(5), oskEnabled: true},
2704+ {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight, oskEnabled: true},
2705+ {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight, oskEnabled: true},
2706+ {tag: "osk not on this screen", windowHeight: units.gu(40), windowY: units.gu(10), targetDisplacement: units.gu(10), oskEnabled: false},
2707+ ]
2708+ }
2709+
2710+ function test_oskDisplacesWindow(data) {
2711+ loadShell("desktop");
2712+ shell.usageScenario = "desktop";
2713+ waitForRendering(shell);
2714+ swipeAwayGreeter();
2715+ shell.oskEnabled = data.oskEnabled;
2716+
2717+ var oldOSKState = SurfaceManager.inputMethodSurface.state;
2718+ SurfaceManager.inputMethodSurface.state = Mir.RestoredState;
2719+ var appRepeater = findChild(shell, "appRepeater");
2720+ var dashAppDelegate = appRepeater.itemAt(0);
2721+ verify(dashAppDelegate);
2722+ dashAppDelegate.requestedHeight = data.windowHeight;
2723+ dashAppDelegate.requestedY = data.windowY;
2724+ SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
2725+ var initialY = dashAppDelegate.y;
2726+ print("intial", initialY, "panel", PanelState.panelHeight);
2727+ verify(initialY > PanelState.panelHeight);
2728+
2729+ SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, root.height / 2, root.width, root.height / 2));
2730+ tryCompare(dashAppDelegate, "y", data.targetDisplacement);
2731+
2732+ SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
2733+ tryCompare(dashAppDelegate, "y", initialY);
2734+ SurfaceManager.inputMethodSurface.state = oldOSKState;
2735+ }
27172736 }
27182737}
tests/qmltests/tst_ShellWithPin.qml+9−11
@@ -393,24 +393,23 @@ Item {
393393 // dialer to a side stage and give access to other apps. So just
394394 // confirm that such an attack doesn't work.
395395
396− var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader")
396+ var stage = findChild(shell, "stage")
397397
398398 // We start in phone mode
399− compare(shell.usageScenario, "phone");
400− compare(applicationsDisplayLoader.usageScenario, "phone");
399+ compare(stage.usageScenario, "phone");
401400
402401 tap(findChild(shell, "emergencyCallLabel"));
403402 confirmLockedApp("dialer-app")
404403
405404 // OK, we're in. Now try (but fail) to switch to tablet mode
406405 shell.usageScenario = "tablet";
407− compare(applicationsDisplayLoader.usageScenario, "phone");
406+ compare(stage.usageScenario, "phone");
408407
409408 // And when we kill the app, we go back to locked tablet mode
410409 killApps()
411410 var greeter = findChild(shell, "greeter")
412411 tryCompare(greeter, "fullyShown", true)
413− compare(applicationsDisplayLoader.usageScenario, "tablet");
412+ compare(stage.usageScenario, "tablet");
414413 }
415414
416415 function test_emergencyDialerIncoming() {
@@ -497,13 +496,13 @@ Item {
497496
498497 function test_suspend() {
499498 var greeter = findChild(shell, "greeter");
500− var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader")
499+ var stage = findChild(shell, "stage")
501500
502501 // Put it to sleep
503502 Powerd.setStatus(Powerd.Off, Powerd.Unknown);
504503
505− // If locked, applicationsDisplayLoader.item.suspended should be true
506− tryCompare(applicationsDisplayLoader.item, "suspended", true);
504+ // If locked, stage.suspended should be true
505+ tryCompare(stage, "suspended", true);
507506
508507 // And wake up
509508 Powerd.setStatus(Powerd.On, Powerd.Unknown);
@@ -513,13 +512,12 @@ Item {
513512 swipeAwayGreeter(true);
514513
515514 // We have a lockscreen, make sure we're still suspended
516− tryCompare(applicationsDisplayLoader.item, "suspended", true);
515+ tryCompare(stage, "suspended", true);
517516
518517 enterPin("1234")
519518
520519 // Now that the lockscreen has gone too, make sure we're waking up
521− tryCompare(applicationsDisplayLoader.item, "suspended", false);
522−
520+ tryCompare(stage, "suspended", false);
523521 }
524522
525523 /* We had a bug (1395075) where if a user kept swiping as the greeter