on xenial help sign in
merged with unified stages
commitd93306eee1ff04dfb0818957c4d3b298834fd658 authorNick Dedekind <nick.dedekind@canonical.com> dateTue 4 Oct 2016, 17:21 (10 years ago) parent52cdce98a50eb4
102 files changed+4391−2982
debian/changelog+5−3 debian/unity8.install+1−1 plugins/Ubuntu/Gestures/TouchGate.cpp+4−1 plugins/Unity/ApplicationMenu/com.ubuntu.MenuRegistrar.xml+13−13 plugins/WindowManager/TopLevelSurfaceList.cpp−5 qml/ApplicationMenus/MenuBarLoader.qml+1−1 qml/CMakeLists.txt+1−1 qml/Components/EdgeBarrier.qml+2 qml/Components/FloatingFlickable.qml+3 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+293 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+1888 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−196 qml/Stages/DesktopStage.qml−839 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/SurfaceManager.cpp+7−6 tests/mocks/Unity/Application/SurfaceManager.h+1−1 tests/mocks/Unity/Application/VirtualKeyboard.cpp+9−7 tests/mocks/Unity/Application/resources/MirSurfaceItem.qml+2−5 tests/mocks/Unity/ApplicationMenu/mockapplicationmenuregistry.cpp+5 tests/mocks/Unity/ApplicationMenu/mockapplicationmenuregistry.h+3 tests/qmltests/ApplicationMenuDataLoader.qml+54 tests/qmltests/CMakeLists.txt+9−10 tests/qmltests/Panel/tst_Panel.qml−3 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+260 tests/qmltests/Stages/tst_DesktopStage.qml → tests/qmltests/Stage/tst_DesktopStage.qml+30−83 tests/qmltests/Stages/tst_PhoneStage.qml → tests/qmltests/Stage/tst_PhoneStage.qml+146−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+103−238 tests/qmltests/Stages/tst_WindowDecoration.qml → tests/qmltests/Stage/tst_WindowDecoration.qml+4−3 tests/qmltests/Stages/tst_WindowResizeArea.qml → tests/qmltests/Stage/tst_WindowResizeArea.qml+40−36 tests/qmltests/Stages/DesktopMenuData.qml−549 tests/qmltests/Stages/tst_DecoratedWindow.qml−82 tests/qmltests/tst_OrientedShell.qml+38−198 tests/qmltests/tst_Shell.qml+182−172 tests/qmltests/tst_ShellWithPin.qml+9−11
debian/changelog+5−3
@@ -1,4 +1,4 @@
1−unity8 (8.14+16.10.20160922-0ubuntu1) yakkety; urgency=medium
1+unity8 (8.14+16.10.20160914-0ubuntu1) yakkety; urgency=medium
22
33 [ Albert Astals Cid ]
44 * LVWPH: update clipItem height when list height changes (LP:
@@ -46,12 +46,14 @@ unity8 (8.14+16.10.20160922-0ubuntu1) yakkety; urgency=medium
4646 the greeter.
4747
4848 [ Lukáš Tinkl ]
49− * Implement edge maximizing (aka window snapping) (LP: #1602628)
5049 * On the PC platform (as opposed to running on $devices), use the
5150 "mute" action instead of silent mode
5251 * Respect Fitt's law wrt the window control buttons in panel (LP:
5352 #1611959)
5453 * Fix 2 failing color-related tests
54+ * Implement edge maximizing (aka window snapping) (LP: #1602628,
55+ #1611859)
56+ * Implement moving windows by Alt + left mouse button
5557
5658 [ Marco Trevisan (Treviño) ]
5759 * Indicators, mocks: add fake indicators menuitem to populate mocks
@@ -73,7 +75,7 @@ unity8 (8.14+16.10.20160922-0ubuntu1) yakkety; urgency=medium
7375 * Update look of infographic a bit
7476 * Make infographic bubbles white even on the default wallpaper.
7577
76− -- Michał Sawicz <michal.sawicz@canonical.com> Thu, 22 Sep 2016 07:46:57 +0000
78+ -- Michał Sawicz <michal.sawicz@canonical.com> Wed, 14 Sep 2016 00:46:45 +0000
7779
7880unity8 (8.14+16.10.20160831.3-0ubuntu1) yakkety; urgency=medium
7981
debian/unity8.install+1−1
@@ -17,7 +17,7 @@ usr/share/unity8/DeviceConfiguration.qml
1717usr/share/unity8/OrientedShell.qml
1818usr/share/unity8/DisabledScreenNotice.qml
1919usr/share/unity8/Shell.qml
20−usr/share/unity8/Stages
20+usr/share/unity8/Stage
2121usr/share/unity8/Tutorial
2222usr/share/unity8/Wizard
2323usr/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/Unity/ApplicationMenu/com.ubuntu.MenuRegistrar.xml+13−13
@@ -4,7 +4,7 @@
44 @mainpage
55
66 An interface to register menus that are associated with a window in an application. The
7− main interface is docuemented here: @ref com::ubuntu::MenuRegistrar.
7+ main interface is documented here: @ref com::ubuntu::MenuRegistrar.
88
99 The actual menus are transported using the gmenumodel protocol
1010 ]]></dox:d>
@@ -16,34 +16,34 @@
1616 </dox:d>
1717 <method name="RegisterAppMenu">
1818 <dox:d><![CDATA[
19− Associates a gmenumodel with a application
19+ Associates a gmenumodel with an application
2020
2121 /note this method assumes that the connection from the caller is the DBus connection
2222 to use for the object. Applications that use multiple DBus connections will need to
23− ensure this method is called with the same connection that implmenets the object.
23+ ensure this method is called with the same connection that implements the object.
2424 ]]></dox:d>
2525 <arg name="pid" type="u" direction="in">
2626 <dox:d>The process ID of the application for which the menu is associated</dox:d>
2727 </arg>
2828 <arg name="menuObjectPath" type="o" direction="in">
29− <dox:d>The object on the dbus interface implementing the gmenumodel interface</dox:d>
29+ <dox:d>The dbus path where the gmenumodel interface for the application menu has been exported</dox:d>
3030 </arg>
3131 <arg name="actionObjectPath" type="o" direction="in">
32− <dox:d>The object on the dbus interface implementing the gactiongroup interface</dox:d>
32+ <dox:d>The dbus path where the gactionmenu interface for the application menu actions has been exported</dox:d>
3333 </arg>
3434 <arg name="service" type="s" direction="in">
35− <dox:d>The address of the connection on DBus (e.g. :1.23 or org.example.service)</dox:d>
35+ <dox:d>The dbus conection name of the client application to be registered (e.g. :1.23 or org.example.service)</dox:d>
3636 </arg>
3737 </method>
3838 <method name="UnregisterAppMenu">
3939 <dox:d>
40− A method to allow removing a application menu from the database.
40+ A method to allow removing an application menu from the database.
4141 </dox:d>
4242 <arg name="pid" type="u" direction="in">
4343 <dox:d>The process id of the application</dox:d>
4444 </arg>
4545 <arg name="menuObjectPath" type="o" direction="in">
46− <dox:d>The object on the dbus interface implementing the gmenumodel interface</dox:d>
46+ <dox:d>The dbus path for the registered application menu to be unregistered</dox:d>
4747 </arg>
4848 </method>
4949
@@ -53,19 +53,19 @@
5353
5454 /note this method assumes that the connection from the caller is the DBus connection
5555 to use for the object. Applications that use multiple DBus connections will need to
56− ensure this method is called with the same connection that implmenets the object.
56+ ensure this method is called with the same connection that implements the object.
5757 ]]></dox:d>
5858 <arg name="surface" type="s" direction="in">
5959 <dox:d>The surface ID of the surface</dox:d>
6060 </arg>
6161 <arg name="menuObjectPath" type="o" direction="in">
62− <dox:d>The object on the dbus interface implementing the gmenumodel interface</dox:d>
62+ <dox:d>The dbus path where the gmenumodel interface for the surface menu has been exported</dox:d>
6363 </arg>
6464 <arg name="actionObjectPath" type="o" direction="in">
65− <dox:d>The object on the dbus interface implementing the gactiongroup interface</dox:d>
65+ <dox:d>The dbus path where the gactionmenu interface for the surface menu actions has been exported</dox:d>
6666 </arg>
6767 <arg name="service" type="s" direction="in">
68− <dox:d>The address of the connection on DBus (e.g. :1.23 or org.example.service)</dox:d>
68+ <dox:d>The dbus conection name of the client application to be registered (e.g. :1.23 or org.example.service)</dox:d>
6969 </arg>
7070 </method>
7171 <method name="UnregisterSurfaceMenu">
@@ -76,7 +76,7 @@
7676 <dox:d>The surface id of the surface</dox:d>
7777 </arg>
7878 <arg name="menuObjectPath" type="o" direction="in">
79− <dox:d>The object on the dbus interface implementing the gmenumodel interface</dox:d>
79+ <dox:d>The dbus path for the registered surface menu to be unregistered</dox:d>
8080 </arg>
8181 </method>
8282 </interface>
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/ApplicationMenus/MenuBarLoader.qml+1−1
@@ -101,7 +101,7 @@ Item {
101101 property bool popupVisible: __popup && __popup.visible
102102
103103 implicitWidth: column.implicitWidth
104− implicitHeight: parent.height
104+ implicitHeight: row.height
105105 enabled: model.sensitive
106106
107107 Keys.forwardTo: [ root ]
qml/CMakeLists.txt+1−1
@@ -13,7 +13,7 @@ set(QML_DIRS
1313 Launcher
1414 Notifications
1515 Panel
16− Stages
16+ Stage
1717 Rotation
1818 Tutorial
1919 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/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 :
@@ -654,7 +543,7 @@ StyledItem {
654543 }
655544 onFocusChanged: {
656545 if (!focus) {
657− applicationsDisplayLoader.focus = true;
546+ stage.focus = true;
658547 }
659548 }
660549
@@ -710,12 +599,12 @@ StyledItem {
710599 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
711600 inputMethod.visible ||
712601 (launcher.shown && !launcher.lockedVisible) ||
713− panel.indicators.shown || stage.dragProgress > 0
602+ panel.indicators.shown || stage.rightEdgeDragProgress > 0
714603 usageScenario: shell.usageScenario
715604 lastInputTimestamp: inputFilter.lastInputTimestamp
716605 launcher: launcher
717606 panel: panel
718− stage: applicationsDisplayLoader.item
607+ stage: stage
719608 }
720609
721610 Wizard {
@@ -806,7 +695,7 @@ StyledItem {
806695 z: dialogs.z + 10
807696 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
808697 Connections {
809− target: applicationsDisplayLoader.item
698+ target: stage
810699 ignoreUnknownSignals: true
811700 onItemSnapshotRequested: itemGrabber.capture(item)
812701 }
@@ -818,7 +707,7 @@ StyledItem {
818707 z: itemGrabber.z + 1
819708 topBoundaryOffset: panel.panelHeight
820709
821− confiningItem: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.itemConfiningMouseCursor : null
710+ confiningItem: stage.itemConfiningMouseCursor
822711
823712 property bool mouseNeverMoved: true
824713 Binding {
@@ -832,10 +721,9 @@ StyledItem {
832721
833722 height: units.gu(3)
834723
835− readonly property var previewRectangle: applicationsDisplayLoader.item && applicationsDisplayLoader.item.previewRectangle &&
836− applicationsDisplayLoader.item.previewRectangle.target &&
837− applicationsDisplayLoader.item.previewRectangle.target.dragging ?
838− applicationsDisplayLoader.item.previewRectangle : null
724+ readonly property var previewRectangle: stage.previewRectangle.target &&
725+ stage.previewRectangle.target.dragging ?
726+ stage.previewRectangle : null
839727
840728 onPushedLeftBoundary: {
841729 if (buttons === Qt.NoButton) {
@@ -846,9 +734,8 @@ StyledItem {
846734 }
847735
848736 onPushedRightBoundary: {
849− if (buttons === Qt.NoButton && applicationsDisplayLoader.item
850− && applicationsDisplayLoader.item.pushRightEdge) {
851− applicationsDisplayLoader.item.pushRightEdge(amount);
737+ if (buttons === Qt.NoButton) {
738+ stage.pushRightEdge(amount);
852739 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
853740 previewRectangle.maximizeRight(amount);
854741 }
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+293
@@ -0,0 +1,293 @@
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+import Unity.ApplicationMenu 0.1
22+import Unity.Indicators 0.1 as Indicators
23+import "../Components/PanelState"
24+
25+FocusScope {
26+ id: root
27+
28+ // The DecoratedWindow takes requestedWidth/requestedHeight and asks its surface to be resized to that
29+ // (minus the window decoration size in case hasDecoration and showDecoration are true)
30+ // The surface might not be able to resize to the requested values. It will return its actual size
31+ // in implicitWidth/implicitHeight.
32+
33+ property alias application: applicationWindow.application
34+ property alias surface: applicationWindow.surface
35+ readonly property alias focusedSurface: applicationWindow.focusedSurface
36+ property alias active: decoration.active
37+ readonly property alias title: applicationWindow.title
38+ property alias maximizeButtonShown: decoration.maximizeButtonShown
39+ property alias interactive: applicationWindow.interactive
40+ readonly property alias orientationChangesEnabled: applicationWindow.orientationChangesEnabled
41+
42+ // Changing this will actually add/remove a decoration, meaning, requestedHeight will take the decoration into account.
43+ property bool hasDecoration: true
44+ // This will temporarily show/hide the decoration without actually changing the surface's dimensions
45+ property real showDecoration: 1
46+ property bool animateDecoration: false
47+ property bool showHighlight: false
48+ property int highlightSize: units.gu(1)
49+ property real shadowOpacity: 0
50+ property bool darkening: false
51+
52+ property real requestedWidth
53+ property real requestedHeight
54+ property real scaleToPreviewProgress: 0
55+ property int scaleToPreviewSize: units.gu(30)
56+
57+ property alias surfaceOrientationAngle: applicationWindow.surfaceOrientationAngle
58+ readonly property real decorationHeight: Math.min(d.visibleDecorationHeight, d.requestedDecorationHeight)
59+ readonly property bool counterRotate: surfaceOrientationAngle != 0 && surfaceOrientationAngle != 180
60+
61+ readonly property int minimumWidth: !counterRotate ? applicationWindow.minimumWidth : applicationWindow.minimumHeight
62+ readonly property int minimumHeight: decorationHeight + (!counterRotate ? applicationWindow.minimumHeight : applicationWindow.minimumWidth)
63+ readonly property int maximumWidth: !counterRotate ? applicationWindow.maximumWidth : applicationWindow.maximumHeight
64+ readonly property int maximumHeight: (root.decorationShown && applicationWindow.maximumHeight > 0 ? decoration.height : 0)
65+ + (!counterRotate ? applicationWindow.maximumHeight : applicationWindow.maximumWidth)
66+ readonly property int widthIncrement: !counterRotate ? applicationWindow.widthIncrement : applicationWindow.heightIncrement
67+ readonly property int heightIncrement: !counterRotate ? applicationWindow.heightIncrement : applicationWindow.widthIncrement
68+
69+ property alias overlayShown: decoration.overlayShown
70+ property alias stageWidth: moveHandler.stageWidth
71+ property alias stageHeight: moveHandler.stageHeight
72+ readonly property alias dragging: moveHandler.dragging
73+
74+ readonly property Item clientAreaItem: applicationWindow
75+
76+ signal closeClicked()
77+ signal maximizeClicked()
78+ signal maximizeHorizontallyClicked()
79+ signal maximizeVerticallyClicked()
80+ signal minimizeClicked()
81+ signal decorationPressed()
82+ signal decorationReleased()
83+
84+ QtObject {
85+ id: d
86+ property int requestedDecorationHeight: root.hasDecoration ? decoration.height : 0
87+ Behavior on requestedDecorationHeight { enabled: root.animateDecoration; UbuntuNumberAnimation { } }
88+
89+ property int visibleDecorationHeight: root.hasDecoration ? root.showDecoration * decoration.height : 0
90+ Behavior on visibleDecorationHeight { enabled: root.animateDecoration; UbuntuNumberAnimation { } }
91+ }
92+
93+ StateGroup {
94+ states: [
95+ State {
96+ name: "normal"; when: root.scaleToPreviewProgress <= 0 && root.application.state === ApplicationInfoInterface.Running
97+ PropertyChanges {
98+ target: root
99+ implicitWidth: counterRotate ? applicationWindow.implicitHeight : applicationWindow.implicitWidth
100+ implicitHeight: root.decorationHeight + (counterRotate ? applicationWindow.implicitWidth: applicationWindow.implicitHeight)
101+ }
102+ },
103+ State {
104+ name: "normalSuspended"; when: root.scaleToPreviewProgress <= 0 && root.application.state !== ApplicationInfoInterface.Running
105+ PropertyChanges {
106+ target: root
107+ implicitWidth: counterRotate ? applicationWindow.requestedHeight : applicationWindow.requestedWidth
108+ implicitHeight: root.decorationHeight + (counterRotate ? applicationWindow.requestedWidth: applicationWindow.requestedHeight)
109+ }
110+// PropertyChanges { target: applicationWindow; anchors.topMargin: (root.height - applicationWindow.implicitHeight) / 2 }
111+// PropertyChanges { target: dropShadow; anchors.topMargin: (root.height - applicationWindow.implicitHeight) / 2 }
112+ },
113+ State {
114+ name: "preview"; when: root.scaleToPreviewProgress > 0 && root.application.state === ApplicationInfoInterface.Running
115+ PropertyChanges {
116+ target: root
117+ implicitWidth: MathUtils.linearAnimation(0, 1, applicationWindow.oldRequestedWidth, root.scaleToPreviewSize, root.scaleToPreviewProgress)
118+ implicitHeight: MathUtils.linearAnimation(0, 1, applicationWindow.oldRequestedHeight, root.scaleToPreviewSize, root.scaleToPreviewProgress)
119+ }
120+ PropertyChanges {
121+ target: applicationWindow;
122+ requestedWidth: applicationWindow.oldRequestedWidth
123+ requestedHeight: applicationWindow.oldRequestedHeight
124+ width: MathUtils.linearAnimation(0, 1, applicationWindow.oldRequestedWidth, applicationWindow.minSize, root.scaleToPreviewProgress)
125+ height: MathUtils.linearAnimation(0, 1, applicationWindow.oldRequestedHeight, applicationWindow.minSize, root.scaleToPreviewProgress)
126+ itemScale: root.implicitWidth / width
127+ }
128+ },
129+ State {
130+ name: "previewSuspended"; when: root.scaleToPreviewProgress > 0 && root.application.state !== ApplicationInfoInterface.Running
131+ extend: "preview"
132+ PropertyChanges { target: applicationWindow;
133+ anchors.verticalCenterOffset: applicationWindow.implicitHeight < applicationWindow.height ?
134+ (root.height / applicationWindow.itemScale - applicationWindow.implicitHeight) / 2
135+ : (root.height / applicationWindow.itemScale - applicationWindow.height) / 2
136+ }
137+// PropertyChanges { target: dropShadow; anchors.topMargin: (root.height - root.implicitHeight) / 2 * (1 - root.scaleToPreviewProgress) }
138+ }
139+ ]
140+ }
141+
142+ Rectangle {
143+ id: selectionHighlight
144+ objectName: "selectionHighlight"
145+ anchors.fill: parent
146+ anchors.margins: -root.highlightSize
147+ color: "white"
148+ opacity: showHighlight ? 0.55 : 0
149+ visible: opacity > 0
150+ }
151+
152+ BorderImage {
153+ id: dropShadow
154+ anchors {
155+ left: parent.left; top: parent.top; right: parent.right
156+ margins: active ? -units.gu(2) : -units.gu(1.5)
157+ }
158+ height: Math.min(applicationWindow.implicitHeight, applicationWindow.height) * applicationWindow.itemScale
159+ + root.decorationHeight * Math.min(1, root.showDecoration) + (active ? units.gu(4) : units.gu(3))
160+ source: "../graphics/dropshadow2gu.sci"
161+ opacity: root.shadowOpacity
162+ }
163+
164+ ApplicationWindow {
165+ id: applicationWindow
166+ objectName: "appWindow"
167+ anchors.verticalCenter: parent.verticalCenter
168+ anchors.verticalCenterOffset: (root.decorationHeight * Math.min(1, root.showDecoration)) / 2
169+ anchors.left: parent.left
170+ width: implicitWidth
171+ height: implicitHeight
172+ requestedHeight: !counterRotate ? root.requestedHeight - d.requestedDecorationHeight : root.requestedWidth
173+ requestedWidth: !counterRotate ? root.requestedWidth : root.requestedHeight - d.requestedDecorationHeight
174+ property int oldRequestedWidth: requestedWidth
175+ property int oldRequestedHeight: requestedHeight
176+ onRequestedWidthChanged: oldRequestedWidth = requestedWidth
177+ onRequestedHeightChanged: oldRequestedHeight = requestedHeight
178+ focus: true
179+
180+ property real itemScale: 1
181+ property real minSize: Math.min(root.scaleToPreviewSize, Math.min(requestedHeight, Math.min(requestedWidth, Math.min(implicitHeight, implicitWidth))))
182+
183+ transform: [
184+ Rotation {
185+ id: rotationTransform
186+ readonly property int rotationAngle: applicationWindow.application &&
187+ applicationWindow.application.rotatesWindowContents
188+ ? ((360 - applicationWindow.surfaceOrientationAngle) % 360) : 0
189+ origin.x: {
190+ if (rotationAngle == 90) return applicationWindow.height / 2;
191+ else if (rotationAngle == 270) return applicationWindow.width / 2;
192+ else if (rotationAngle == 180) return applicationWindow.width / 2;
193+ else return 0;
194+ }
195+ origin.y: {
196+ if (rotationAngle == 90) return applicationWindow.height / 2;
197+ else if (rotationAngle == 270) return applicationWindow.width / 2;
198+ else if (rotationAngle == 180) return applicationWindow.height / 2;
199+ else return 0;
200+ }
201+ angle: rotationAngle
202+ },
203+ Scale {
204+ origin.y: (applicationWindow.implicitHeight < applicationWindow.height ? applicationWindow.implicitHeight : applicationWindow.height) / 2
205+ xScale: applicationWindow.itemScale
206+ yScale: applicationWindow.itemScale
207+ }
208+ ]
209+ }
210+
211+ MouseArea {
212+ anchors { left: parent.left; top: parent.top; right: parent.right }
213+ height: units.gu(3)
214+
215+ opacity: root.hasDecoration ? Math.min(1, root.showDecoration) : 0
216+
217+ Behavior on opacity { UbuntuNumberAnimation { } }
218+
219+ drag.target: Item {}
220+ drag.filterChildren: true
221+ drag.threshold: 0
222+
223+ onPressed: root.decorationPressed();
224+ onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
225+ onPositionChanged: moveHandler.handlePositionChanged(mouse)
226+ onReleased: {
227+ root.decorationReleased();
228+ moveHandler.handleReleased();
229+ }
230+
231+ WindowDecoration {
232+ id: decoration
233+ target: root.parent || null
234+ objectName: "appWindowDecoration"
235+ anchors.fill: parent
236+ title: applicationWindow.title
237+
238+ onCloseClicked: root.closeClicked();
239+ onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); }
240+ onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); }
241+ onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }
242+ onMinimizeClicked: root.minimizeClicked();
243+
244+ enableMenus: {
245+ return active &&
246+ surface &&
247+ (PanelState.focusedPersistentSurfaceId !== surface.persistentId ||
248+ (PanelState.focusedPersistentSurfaceId === surface.persistentId && !PanelState.focusedSurfaceMaximized))
249+ }
250+ menu: sharedAppModel.model
251+
252+ Indicators.SharedUnityMenuModel {
253+ id: sharedAppModel
254+ property var menus: surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : []
255+ property var menuService: menus.length > 0 ? menus[0] : undefined
256+
257+ onMenuServiceChanged: console.log("MENU!", menuService.service, menuService.menuPath, menuService.actionPath)
258+
259+ busName: menuService ? menuService.service : ""
260+ menuObjectPath: menuService && menuService.menuPath ? menuService.menuPath : ""
261+ actions: menuService && menuService.actionPath ? { "unity": menuService.actionPath } : {}
262+ }
263+
264+ Connections {
265+ target: ApplicationMenuRegistry
266+ onSurfaceMenuRegistered: {
267+ if (surface && surfaceId === surface.persistentId) {
268+ sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] });
269+ }
270+ }
271+ onSurfaceMenuUnregistered: {
272+ if (surface && surfaceId === surface.persistentId) {
273+ sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] });
274+ }
275+ }
276+ }
277+ }
278+ }
279+
280+ MoveHandler {
281+ id: moveHandler
282+ objectName: "moveHandler"
283+ target: root.parent
284+ buttonsWidth: decoration.buttonsWidth
285+ }
286+
287+ Rectangle {
288+ anchors.fill: parent
289+ color: "black"
290+ opacity: root.darkening && !root.showHighlight ? 0.05 : 0
291+ Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
292+ }
293+}
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.y, 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 - 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+1888
@@ -0,0 +1,1888 @@
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.shellOrientation == Qt.LandscapeOrientation ||
232+ root.shellOrientation == Qt.InvertedLandscapeOrientation
233+
234+ property var mainStageDelegate: null
235+ property var sideStageDelegate: null
236+ property int mainStageItemId: 0
237+ property int sideStageItemId: 0
238+ property string mainStageAppId: ""
239+ property string sideStageAppId: ""
240+
241+ onSideStageDelegateChanged: {
242+ if (!sideStageDelegate) {
243+ sideStage.hide();
244+ }
245+ }
246+
247+ function updateMainAndSideStageIndexes() {
248+ if (root.mode != "stagedWithSideStage") {
249+ priv.sideStageDelegate = null;
250+ priv.sideStageItemId = 0;
251+ priv.sideStageAppId = "";
252+ priv.mainStageDelegate = appRepeater.itemAt(0);
253+ priv.mainStageAppId = topLevelSurfaceList.idAt(0);
254+ priv.mainStageAppId = topLevelSurfaceList.applicationAt(0) ? topLevelSurfaceList.applicationAt(0).appId : ""
255+ return;
256+ }
257+
258+ var choseMainStage = false;
259+ var choseSideStage = false;
260+
261+ if (!root.topLevelSurfaceList)
262+ return;
263+
264+ for (var i = 0; i < appRepeater.count && (!choseMainStage || !choseSideStage); ++i) {
265+ var appDelegate = appRepeater.itemAt(i);
266+ if (sideStage.shown && appDelegate.stage == ApplicationInfoInterface.SideStage
267+ && !choseSideStage) {
268+ priv.sideStageDelegate = appDelegate
269+ priv.sideStageItemId = root.topLevelSurfaceList.idAt(i);
270+ priv.sideStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
271+ choseSideStage = true;
272+ } else if (!choseMainStage && appDelegate.stage == ApplicationInfoInterface.MainStage) {
273+ priv.mainStageDelegate = appDelegate;
274+ priv.mainStageItemId = root.topLevelSurfaceList.idAt(i);
275+ priv.mainStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
276+ choseMainStage = true;
277+ }
278+ }
279+ if (!choseMainStage && priv.mainStageDelegate) {
280+ priv.mainStageDelegate = null;
281+ priv.mainStageItemId = 0;
282+ priv.mainStageAppId = "";
283+ }
284+ if (!choseSideStage && priv.sideStageDelegate) {
285+ priv.sideStageDelegate = null;
286+ priv.sideStageItemId = 0;
287+ priv.sideStageAppId = "";
288+ }
289+ }
290+
291+ property int nextInStack: {
292+ var mainStageIndex = priv.mainStageDelegate ? priv.mainStageDelegate.itemIndex : -1;
293+ var sideStageIndex = priv.sideStageDelegate ? priv.sideStageDelegate.itemIndex : -1;
294+ if (sideStageIndex == -1) {
295+ return topLevelSurfaceList.count > 1 ? 1 : -1;
296+ }
297+ if (mainStageIndex == 0 || sideStageIndex == 0) {
298+ if (mainStageIndex == 1 || sideStageIndex == 1) {
299+ return topLevelSurfaceList.count > 2 ? 2 : -1;
300+ }
301+ return 1;
302+ }
303+ return -1;
304+ }
305+
306+ readonly property real virtualKeyboardHeight: root.inputMethodRect.height
307+ }
308+
309+ Component.onCompleted: priv.updateMainAndSideStageIndexes();
310+
311+ Connections {
312+ target: PanelState
313+ onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
314+ onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.minimize(); } }
315+ onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.restoreFromMaximized(); } }
316+ }
317+
318+ Binding {
319+ target: PanelState
320+ property: "decorationsVisible"
321+ value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized // FIXME for Locally integrated menus
322+ }
323+
324+ Binding {
325+ target: PanelState
326+ property: "title"
327+ value: {
328+ if (priv.focusedAppDelegate !== null) {
329+ if (priv.focusedAppDelegate.maximized)
330+ return priv.focusedAppDelegate.title
331+ else
332+ return priv.focusedAppDelegate.appName
333+ }
334+ return ""
335+ }
336+ when: priv.focusedAppDelegate
337+ }
338+
339+ Binding {
340+ target: PanelState
341+ property: "focusedPersistentSurfaceId"
342+ value: {
343+ if (priv.focusedAppDelegate !== null) {
344+ if (priv.focusedAppDelegate.surface) {
345+ return priv.focusedAppDelegate.surface.persistentId;
346+ }
347+ }
348+ return "";
349+ }
350+ when: priv.focusedAppDelegate
351+ }
352+
353+ Binding {
354+ target: PanelState
355+ property: "dropShadow"
356+ value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppDelegate !== null && mode == "windowed"
357+ }
358+
359+ Binding {
360+ target: PanelState
361+ property: "closeButtonShown"
362+ value: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized && !priv.focusedAppDelegate.isDash
363+ }
364+
365+ Component.onDestruction: {
366+ PanelState.title = "";
367+ PanelState.decorationsVisible = false;
368+ PanelState.dropShadow = false;
369+ }
370+
371+ Instantiator {
372+ model: root.applicationManager
373+ delegate: QtObject {
374+ property var stateBinding: Binding {
375+ readonly property bool isDash: model.application ? model.application.appId == "unity8-dash" : false
376+ target: model.application
377+ property: "requestedState"
378+
379+ // TODO: figure out some lifecycle policy, like suspending minimized apps
380+ // or something if running windowed.
381+ // TODO: If the device has a dozen suspended apps because it was running
382+ // in staged mode, when it switches to Windowed mode it will suddenly
383+ // resume all those apps at once. We might want to avoid that.
384+ value: root.mode === "windowed"
385+ || (isDash && root.keepDashRunning)
386+ || (!root.suspended && model.application && priv.focusedAppDelegate &&
387+ (priv.focusedAppDelegate.appId === model.application.appId ||
388+ priv.mainStageAppId === model.application.appId ||
389+ priv.sideStageAppId === model.application.appId))
390+ ? ApplicationInfoInterface.RequestedRunning
391+ : ApplicationInfoInterface.RequestedSuspended
392+ }
393+
394+ property var lifecycleBinding: Binding {
395+ target: model.application
396+ property: "exemptFromLifecycle"
397+ value: model.application
398+ ? (!model.application.isTouchApp || isExemptFromLifecycle(model.application.appId))
399+ : false
400+ }
401+ }
402+ }
403+
404+ Binding {
405+ target: MirFocusController
406+ property: "focusedSurface"
407+ value: priv.focusedAppDelegate ? priv.focusedAppDelegate.focusedSurface : null
408+ when: !appRepeater.startingUp && root.parent
409+ }
410+
411+ states: [
412+ State {
413+ name: "spread"; when: priv.goneToSpread
414+ PropertyChanges { target: floatingFlickable; enabled: true }
415+ PropertyChanges { target: spreadItem; focus: true }
416+ PropertyChanges { target: hoverMouseArea; enabled: true }
417+ PropertyChanges { target: rightEdgeDragArea; enabled: false }
418+ PropertyChanges { target: cancelSpreadMouseArea; enabled: true }
419+ },
420+ State {
421+ name: "stagedRightEdge"; when: (rightEdgeDragArea.dragging || edgeBarrier.progress > 0) && root.mode == "staged"
422+ },
423+ State {
424+ name: "sideStagedRightEdge"; when: (rightEdgeDragArea.dragging || edgeBarrier.progress > 0) && root.mode == "stagedWithSideStage"
425+ PropertyChanges {
426+ target: sideStage
427+ opacity: priv.sideStageDelegate.x === sideStage.x ? 1 : 0
428+ visible: true
429+ }
430+ },
431+ State {
432+ name: "windowedRightEdge"; when: (rightEdgeDragArea.dragging || edgeBarrier.progress > 0) && root.mode == "windowed"
433+ },
434+ State {
435+ name: "staged"; when: root.mode === "staged"
436+ },
437+ State {
438+ name: "stagedWithSideStage"; when: root.mode === "stagedWithSideStage"
439+ PropertyChanges { target: triGestureArea; enabled: true }
440+ PropertyChanges { target: sideStage; visible: true }
441+ },
442+ State {
443+ name: "windowed"; when: root.mode === "windowed"
444+ }
445+ ]
446+ transitions: [
447+ Transition {
448+ from: "stagedRightEdge,sideStagedRightEdge,windowedRightEdge"; to: "spread"
449+ PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
450+ },
451+ Transition {
452+ to: "spread"
453+ PropertyAction { target: spreadItem; property: "highlightedIndex"; value: appRepeater.count > 1 ? 1 : 0 }
454+ PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
455+ },
456+ Transition {
457+ from: "spread"
458+ SequentialAnimation {
459+ ScriptAction {
460+ script: {
461+ var item = appRepeater.itemAt(Math.max(0, spreadItem.highlightedIndex));
462+ if (item.stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
463+ sideStage.show();
464+ }
465+ item.playFocusAnimation();
466+ }
467+ }
468+ PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
469+ }
470+ },
471+ Transition {
472+ to: "stagedRightEdge"
473+ PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
474+ },
475+ Transition {
476+ to: "stagedWithSideStage"
477+ ScriptAction { script: priv.updateMainAndSideStageIndexes(); }
478+ }
479+
480+ ]
481+
482+ MouseArea {
483+ id: cancelSpreadMouseArea
484+ anchors.fill: parent
485+ enabled: false
486+ onClicked: priv.goneToSpread = false
487+ }
488+
489+ FocusScope {
490+ id: appContainer
491+ objectName: "appContainer"
492+ anchors.fill: parent
493+ focus: true
494+
495+ Wallpaper {
496+ id: wallpaper
497+ anchors.fill: parent
498+ source: root.background
499+ // Make sure it's the lowest item. Due to the left edge drag we sometimes need
500+ // to put the dash at -1 and we don't want it behind the Wallpaper
501+ z: -2
502+ }
503+
504+ Spread {
505+ id: spreadItem
506+ objectName: "spreadItem"
507+ anchors.fill: appContainer
508+ leftMargin: root.leftMargin
509+ model: root.topLevelSurfaceList
510+ spreadFlickable: floatingFlickable
511+ z: 10
512+
513+ onLeaveSpread: {
514+ priv.goneToSpread = false;
515+ }
516+ }
517+
518+ Connections {
519+ target: root.topLevelSurfaceList
520+ onListChanged: priv.updateMainAndSideStageIndexes()
521+ }
522+
523+
524+ DropArea {
525+ objectName: "MainStageDropArea"
526+ anchors {
527+ left: parent.left
528+ top: parent.top
529+ bottom: parent.bottom
530+ }
531+ width: appContainer.width - sideStage.width
532+ enabled: sideStage.enabled
533+
534+ onDropped: {
535+ drop.source.appDelegate.saveStage(ApplicationInfoInterface.MainStage);
536+ drop.source.appDelegate.focus = true;
537+ }
538+ keys: "SideStage"
539+ }
540+
541+ SideStage {
542+ id: sideStage
543+ objectName: "sideStage"
544+ shown: false
545+ height: appContainer.height
546+ x: appContainer.width - width
547+ visible: false
548+ Behavior on opacity { UbuntuNumberAnimation {} }
549+ z: {
550+ if (!priv.mainStageItemId) return 0;
551+
552+ if (priv.sideStageItemId && priv.nextInStack > 0) {
553+
554+ // Due the order in which bindings are evaluated, this might be triggered while shuffling
555+ // the list and index doesn't yet match with itemIndex (even though itemIndex: index)
556+ // Let's walk the list and compare itemIndex to make sure we have the correct one.
557+ var nextDelegateInStack = -1;
558+ for (var i = 0; i < appRepeater.count; i++) {
559+ if (appRepeater.itemAt(i).itemIndex == priv.nextInStack) {
560+ nextDelegateInStack = appRepeater.itemAt(i);
561+ break;
562+ }
563+ }
564+
565+ if (nextDelegateInStack.stage === ApplicationInfoInterface.MainStage) {
566+ // if the next app in stack is a main stage app, put the sidestage on top of it.
567+ return 2;
568+ }
569+ return 1;
570+ }
571+
572+ return 1;
573+ }
574+
575+ onShownChanged: {
576+ if (!shown && priv.mainStageDelegate) {
577+ priv.mainStageDelegate.claimFocus();
578+ }
579+ }
580+
581+ DropArea {
582+ id: sideStageDropArea
583+ objectName: "SideStageDropArea"
584+ anchors.fill: parent
585+
586+ property bool dropAllowed: true
587+
588+ onEntered: {
589+ dropAllowed = drag.keys != "Disabled";
590+ }
591+ onExited: {
592+ dropAllowed = true;
593+ }
594+ onDropped: {
595+ if (drop.keys == "MainStage") {
596+ drop.source.appDelegate.saveStage(ApplicationInfoInterface.SideStage);
597+ drop.source.appDelegate.focus = true;
598+ }
599+ }
600+ drag {
601+ onSourceChanged: {
602+ if (!sideStageDropArea.drag.source) {
603+ dropAllowed = true;
604+ }
605+ }
606+ }
607+ }
608+ }
609+
610+ TopLevelSurfaceRepeater {
611+ id: appRepeater
612+ model: topLevelSurfaceList
613+ objectName: "appRepeater"
614+
615+ delegate: FocusScope {
616+ id: appDelegate
617+ objectName: "appDelegate_" + model.id
618+ property int itemIndex: index // We need this from outside the repeater
619+ // z might be overriden in some cases by effects, but we need z ordering
620+ // to calculate occlusion detection
621+ property int normalZ: topLevelSurfaceList.count - index
622+ onNormalZChanged: {
623+ if (visuallyMaximized) {
624+ priv.updateForegroundMaximizedApp();
625+ }
626+ }
627+ z: normalZ
628+
629+ // Normally we want x/y where we request it to be. Width/height of our delegate will
630+ // match what the actual surface size is.
631+ // Don't write to those, they will be set by states
632+ x: requestedX
633+ y: requestedY
634+ width: decoratedWindow.implicitWidth
635+ height: decoratedWindow.implicitHeight
636+
637+ // requestedX/Y/width/height is what we ask the actual surface to be.
638+ // Do not write to those, they will be set by states
639+ property real requestedX: windowedX
640+ property real requestedY: windowedY
641+ property real requestedWidth: windowedWidth
642+ property real requestedHeight: windowedHeight
643+
644+ // In those are for windowed mode. Those values basically store the window's properties
645+ // when having a floating window. If you want to move/resize a window in normal mode, this is what you want to write to.
646+ property real windowedX
647+ property real windowedY
648+ property real windowedWidth
649+ property real windowedHeight
650+
651+ // unlike windowedX/Y, this is the last known grab position before being pushed against edges/corners
652+ // when restoring, the window should return to these, not to the place where it was dropped near the edge
653+ property real restoredX
654+ property real restoredY
655+
656+ Binding {
657+ target: appDelegate
658+ property: "y"
659+ value: appDelegate.requestedY -
660+ Math.min(appDelegate.requestedY - PanelState.panelHeight,
661+ Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
662+ when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
663+ && root.inputMethodRect.height > 0
664+
665+ }
666+
667+ Behavior on x { id: xBehavior; enabled: priv.closingIndex >= 0; UbuntuNumberAnimation { onRunningChanged: if (!running) priv.closingIndex = -1} }
668+
669+ Connections {
670+ target: root
671+ onShellOrientationAngleChanged: {
672+ // at this point decoratedWindow.surfaceOrientationAngle is the old shellOrientationAngle
673+ if (application && application.rotatesWindowContents) {
674+ if (root.state == "windowed") {
675+ var angleDiff = decoratedWindow.surfaceOrientationAngle - shellOrientationAngle;
676+ angleDiff = (360 + angleDiff) % 360;
677+ if (angleDiff === 90 || angleDiff === 270) {
678+ var aux = decoratedWindow.requestedHeight;
679+ decoratedWindow.requestedHeight = decoratedWindow.requestedWidth + decoratedWindow.decorationHeight;
680+ decoratedWindow.requestedWidth = aux - decoratedWindow.decorationHeight;
681+ }
682+ }
683+ decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
684+ } else {
685+ decoratedWindow.surfaceOrientationAngle = 0;
686+ }
687+ }
688+ }
689+ Connections {
690+ target: priv
691+ onSideStageEnabledChanged: refreshStage()
692+ }
693+
694+ readonly property alias application: decoratedWindow.application
695+ readonly property alias minimumWidth: decoratedWindow.minimumWidth
696+ readonly property alias minimumHeight: decoratedWindow.minimumHeight
697+ readonly property alias maximumWidth: decoratedWindow.maximumWidth
698+ readonly property alias maximumHeight: decoratedWindow.maximumHeight
699+ readonly property alias widthIncrement: decoratedWindow.widthIncrement
700+ readonly property alias heightIncrement: decoratedWindow.heightIncrement
701+
702+ readonly property bool maximized: windowState === WindowStateStorage.WindowStateMaximized
703+ readonly property bool maximizedLeft: windowState === WindowStateStorage.WindowStateMaximizedLeft
704+ readonly property bool maximizedRight: windowState === WindowStateStorage.WindowStateMaximizedRight
705+ readonly property bool maximizedHorizontally: windowState === WindowStateStorage.WindowStateMaximizedHorizontally
706+ readonly property bool maximizedVertically: windowState === WindowStateStorage.WindowStateMaximizedVertically
707+ readonly property bool maximizedTopLeft: windowState === WindowStateStorage.WindowStateMaximizedTopLeft
708+ readonly property bool maximizedTopRight: windowState === WindowStateStorage.WindowStateMaximizedTopRight
709+ readonly property bool maximizedBottomLeft: windowState === WindowStateStorage.WindowStateMaximizedBottomLeft
710+ readonly property bool maximizedBottomRight: windowState === WindowStateStorage.WindowStateMaximizedBottomRight
711+ readonly property bool anyMaximized: maximized || maximizedLeft || maximizedRight || maximizedHorizontally || maximizedVertically ||
712+ maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
713+
714+ readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
715+ readonly property bool fullscreen: surface ? surface.state === Mir.FullscreenState : application.fullscreen
716+
717+ readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
718+ readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
719+ (maximumHeight == 0 || maximumHeight >= appContainer.height)
720+ readonly property bool canBeCornerMaximized: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
721+ (maximumHeight == 0 || maximumHeight >= appContainer.height/2)
722+ readonly property bool canBeMaximizedHorizontally: maximumWidth == 0 || maximumWidth >= appContainer.width
723+ readonly property bool canBeMaximizedVertically: maximumHeight == 0 || maximumHeight >= appContainer.height
724+ readonly property alias orientationChangesEnabled: decoratedWindow.orientationChangesEnabled
725+
726+ property int windowState: WindowStateStorage.WindowStateNormal
727+ property bool animationsEnabled: true
728+ property alias title: decoratedWindow.title
729+ readonly property string appName: model.application ? model.application.name : ""
730+ property bool visuallyMaximized: false
731+ property bool visuallyMinimized: false
732+
733+ property int stage: ApplicationInfoInterface.MainStage
734+ function saveStage(newStage) {
735+ appDelegate.stage = newStage;
736+ WindowStateStorage.saveStage(appId, newStage);
737+ priv.updateMainAndSideStageIndexes()
738+ }
739+
740+ readonly property var surface: model.surface
741+ readonly property alias resizeArea: resizeArea
742+ readonly property alias focusedSurface: decoratedWindow.focusedSurface
743+ readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
744+
745+ readonly property string appId: model.application.appId
746+ readonly property bool isDash: appId == "unity8-dash"
747+ readonly property alias clientAreaItem: decoratedWindow.clientAreaItem
748+
749+ function claimFocus() {
750+ if (root.state == "spread") {
751+ spreadItem.highlightedIndex = index
752+ priv.goneToSpread = false;
753+ }
754+ if (root.mode == "stagedWithSideStage") {
755+ if (appDelegate.stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
756+ sideStage.show();
757+ }
758+ priv.updateMainAndSideStageIndexes();
759+ }
760+
761+ if (root.mode == "windowed") {
762+ appDelegate.restore(true /* animated */, appDelegate.windowState);
763+ } else {
764+ appDelegate.focus = true;
765+ }
766+ }
767+ Connections {
768+ target: model.surface
769+ onFocusRequested: {
770+ claimFocus();
771+ }
772+ }
773+ Connections {
774+ target: model.application
775+ onFocusRequested: {
776+ if (!model.surface) {
777+ // when an app has no surfaces, we assume there's only one entry representing it:
778+ // this delegate.
779+ claimFocus();
780+ } else {
781+ // if the application has surfaces, focus request should be at surface-level.
782+ }
783+ }
784+ }
785+
786+ onFocusChanged: {
787+ if (appRepeater.startingUp)
788+ return;
789+
790+ if (focus) {
791+ topLevelSurfaceList.raiseId(model.id);
792+ priv.focusedAppDelegate = appDelegate;
793+ } else if (!focus && priv.focusedAppDelegate === appDelegate && root.state != "spread") {
794+ priv.focusedAppDelegate = null;
795+ // FIXME: No idea why the Binding{} doens't update when focusedAppDelegate turns null
796+ MirFocusController.focusedSurface = null;
797+ }
798+ }
799+ Component.onCompleted: {
800+ if (application && application.rotatesWindowContents) {
801+ decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
802+ } else {
803+ decoratedWindow.surfaceOrientationAngle = 0;
804+ }
805+
806+ // First, cascade the newly created window, relative to the currently/old focused window.
807+ windowedX = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedX + units.gu(3) : (normalZ - 1) * units.gu(3)
808+ windowedY = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedY + units.gu(3) : normalZ * units.gu(3)
809+ // Now load any saved state. This needs to happen *after* the cascading!
810+ resizeArea.loadWindowState();
811+
812+ // NB: We're differentiating if this delegate was created in response to a new entry in the model
813+ // or if the Repeater is just populating itself with delegates to match the model it received.
814+ if (!appRepeater.startingUp) {
815+ // a top level window is always the focused one when it first appears, unfocusing
816+ // any preexisting one
817+ if (root.state == "spread") {
818+ spreadItem.highlightedIndex = index;
819+ }
820+ claimFocus();
821+ }
822+
823+ refreshStage();
824+ _constructing = false;
825+ }
826+ Component.onDestruction: {
827+ if (!root.parent) {
828+ // This stage is about to be destroyed. Don't mess up with the model at this point
829+ return;
830+ }
831+
832+ if (visuallyMaximized) {
833+ priv.updateForegroundMaximizedApp();
834+ }
835+
836+ if (focus) {
837+ // focus some other window
838+ for (var i = 0; i < appRepeater.count; i++) {
839+ var appDelegate = appRepeater.itemAt(i);
840+ if (appDelegate && !appDelegate.minimized && i != index) {
841+ appDelegate.focus = true;
842+ return;
843+ }
844+ }
845+ }
846+ }
847+
848+ onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
849+
850+ property bool _constructing: true;
851+ onStageChanged: {
852+ if (!_constructing) {
853+ priv.updateMainAndSideStageIndexes();
854+ }
855+ }
856+
857+ visible: (
858+ !visuallyMinimized
859+ && !greeter.fullyShown
860+ && (priv.foregroundMaximizedAppDelegate === null || priv.foregroundMaximizedAppDelegate.normalZ <= z)
861+ )
862+ || appDelegate.fullscreen
863+ || focusAnimation.running || rightEdgeFocusAnimation.running || hidingAnimation.running
864+
865+ function close() {
866+ model.surface.close();
867+ }
868+
869+ function maximize(animated) {
870+ animationsEnabled = (animated === undefined) || animated;
871+ windowState = WindowStateStorage.WindowStateMaximized;
872+ }
873+ function maximizeLeft(animated) {
874+ animationsEnabled = (animated === undefined) || animated;
875+ windowState = WindowStateStorage.WindowStateMaximizedLeft;
876+ }
877+ function maximizeRight(animated) {
878+ animationsEnabled = (animated === undefined) || animated;
879+ windowState = WindowStateStorage.WindowStateMaximizedRight;
880+ }
881+ function maximizeHorizontally(animated) {
882+ animationsEnabled = (animated === undefined) || animated;
883+ windowState = WindowStateStorage.WindowStateMaximizedHorizontally;
884+ }
885+ function maximizeVertically(animated) {
886+ animationsEnabled = (animated === undefined) || animated;
887+ windowState = WindowStateStorage.WindowStateMaximizedVertically;
888+ }
889+ function maximizeTopLeft(animated) {
890+ animationsEnabled = (animated === undefined) || animated;
891+ windowState = WindowStateStorage.WindowStateMaximizedTopLeft;
892+ }
893+ function maximizeTopRight(animated) {
894+ animationsEnabled = (animated === undefined) || animated;
895+ windowState = WindowStateStorage.WindowStateMaximizedTopRight;
896+ }
897+ function maximizeBottomLeft(animated) {
898+ animationsEnabled = (animated === undefined) || animated;
899+ windowState = WindowStateStorage.WindowStateMaximizedBottomLeft;
900+ }
901+ function maximizeBottomRight(animated) {
902+ animationsEnabled = (animated === undefined) || animated;
903+ windowState = WindowStateStorage.WindowStateMaximizedBottomRight;
904+ }
905+ function minimize(animated) {
906+ animationsEnabled = (animated === undefined) || animated;
907+ windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
908+ }
909+ function restoreFromMaximized(animated) {
910+ animationsEnabled = (animated === undefined) || animated;
911+ windowState = WindowStateStorage.WindowStateRestored;
912+ }
913+ function restore(animated,state) {
914+ animationsEnabled = (animated === undefined) || animated;
915+ windowState = state || WindowStateStorage.WindowStateRestored;
916+ windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
917+ focus = true;
918+ }
919+
920+ function playFocusAnimation() {
921+ if (state == "stagedRightEdge") {
922+ // TODO: Can we drop this if and find something that always works?
923+ if (root.mode == "staged") {
924+ rightEdgeFocusAnimation.targetX = 0
925+ rightEdgeFocusAnimation.start()
926+ } else if (root.mode == "stagedWithSideStage") {
927+ rightEdgeFocusAnimation.targetX = appDelegate.stage == ApplicationInfoInterface.SideStage ? sideStage.x : 0
928+ rightEdgeFocusAnimation.start()
929+ }
930+ } else if (state == "windowedRightEdge") {
931+ claimFocus();
932+ } else {
933+ focusAnimation.start()
934+ }
935+ }
936+ function playHidingAnimation() {
937+ if (state != "windowedRightEdge") {
938+ hidingAnimation.start()
939+ }
940+ }
941+
942+ function refreshStage() {
943+ var newStage = ApplicationInfoInterface.MainStage;
944+ if (priv.sideStageEnabled) { // we're in lanscape rotation.
945+ if (!isDash && application && application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
946+ var defaultStage = ApplicationInfoInterface.SideStage; // if application supports portrait, it defaults to sidestage.
947+ if (application.supportedOrientations & (Qt.LandscapeOrientation|Qt.InvertedLandscapeOrientation)) {
948+ // if it supports lanscape, it defaults to mainstage.
949+ defaultStage = ApplicationInfoInterface.MainStage;
950+ }
951+ newStage = WindowStateStorage.getStage(application.appId, defaultStage);
952+ }
953+ }
954+
955+ stage = newStage;
956+ if (focus && stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
957+ sideStage.show();
958+ }
959+ }
960+
961+ UbuntuNumberAnimation {
962+ id: focusAnimation
963+ target: appDelegate
964+ property: "scale"
965+ from: 0.98
966+ to: 1
967+ duration: UbuntuAnimation.SnapDuration
968+ onStarted: {
969+ topLevelSurfaceList.raiseId(model.id);
970+ }
971+ onStopped: {
972+ appDelegate.claimFocus();
973+ }
974+ }
975+ ParallelAnimation {
976+ id: rightEdgeFocusAnimation
977+ property int targetX: 0
978+ UbuntuNumberAnimation { target: appDelegate; properties: "x"; to: rightEdgeFocusAnimation.targetX; duration: priv.animationDuration }
979+ UbuntuNumberAnimation { target: decoratedWindow; properties: "angle"; to: 0; duration: priv.animationDuration }
980+ UbuntuNumberAnimation { target: decoratedWindow; properties: "itemScale"; to: 1; duration: priv.animationDuration }
981+ onStopped: {
982+ appDelegate.focus = true
983+ }
984+ }
985+ ParallelAnimation {
986+ id: hidingAnimation
987+ UbuntuNumberAnimation { target: appDelegate; property: "opacity"; to: 0; duration: priv.animationDuration }
988+ onStopped: appDelegate.opacity = 1
989+ }
990+
991+ SpreadMaths {
992+ id: spreadMaths
993+ spread: spreadItem
994+ itemIndex: index
995+ flickable: floatingFlickable
996+ }
997+ StageMaths {
998+ id: stageMaths
999+ sceneWidth: root.width
1000+ stage: appDelegate.stage
1001+ thisDelegate: appDelegate
1002+ mainStageDelegate: priv.mainStageDelegate
1003+ sideStageDelegate: priv.sideStageDelegate
1004+ sideStageWidth: sideStage.panelWidth
1005+ sideStageX: sideStage.x
1006+ itemIndex: appDelegate.itemIndex
1007+ nextInStack: priv.nextInStack
1008+ leftEdgeDragProgress: root.leftEdgeDragProgress
1009+ }
1010+
1011+ StagedRightEdgeMaths {
1012+ id: stagedRightEdgeMaths
1013+ sceneWidth: appContainer.width - root.leftMargin
1014+ sceneHeight: appContainer.height
1015+ isMainStageApp: priv.mainStageDelegate == appDelegate
1016+ isSideStageApp: priv.sideStageDelegate == appDelegate
1017+ sideStageWidth: sideStage.width
1018+ sideStageOpen: sideStage.shown
1019+ itemIndex: index
1020+ nextInStack: priv.nextInStack
1021+ progress: 0
1022+ targetHeight: spreadItem.stackHeight
1023+ targetX: spreadMaths.targetX
1024+ startY: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1025+ targetY: spreadMaths.targetY
1026+ targetAngle: spreadMaths.targetAngle
1027+ targetScale: spreadMaths.targetScale
1028+ shuffledZ: stageMaths.itemZ
1029+ breakPoint: spreadItem.rightEdgeBreakPoint
1030+ }
1031+
1032+ WindowedRightEdgeMaths {
1033+ id: windowedRightEdgeMaths
1034+ itemIndex: index
1035+ startWidth: appDelegate.requestedWidth
1036+ startHeight: appDelegate.requestedHeight
1037+ targetHeight: spreadItem.stackHeight
1038+ targetX: spreadMaths.targetX
1039+ targetY: spreadMaths.targetY
1040+ normalZ: appDelegate.normalZ
1041+ targetAngle: spreadMaths.targetAngle
1042+ targetScale: spreadMaths.targetScale
1043+ breakPoint: spreadItem.rightEdgeBreakPoint
1044+ }
1045+
1046+ states: [
1047+ State {
1048+ name: "spread"; when: root.state == "spread"
1049+ PropertyChanges {
1050+ target: decoratedWindow;
1051+ showDecoration: false;
1052+ angle: spreadMaths.targetAngle
1053+ itemScale: spreadMaths.targetScale
1054+ scaleToPreviewSize: spreadItem.stackHeight
1055+ scaleToPreviewProgress: 1
1056+ hasDecoration: root.mode === "windowed"
1057+ shadowOpacity: spreadMaths.shadowOpacity
1058+ showHighlight: spreadItem.highlightedIndex === index
1059+ darkening: spreadItem.highlightedIndex >= 0
1060+ anchors.topMargin: dragArea.distance
1061+ }
1062+ PropertyChanges {
1063+ target: appDelegate
1064+ x: spreadMaths.targetX
1065+ y: spreadMaths.targetY
1066+ z: index
1067+ height: spreadItem.spreadItemHeight
1068+ requestedWidth: decoratedWindow.oldRequestedWidth
1069+ requestedHeight: decoratedWindow.oldRequestedHeight
1070+ visible: spreadMaths.itemVisible
1071+ }
1072+ PropertyChanges { target: dragArea; enabled: true }
1073+ PropertyChanges { target: windowInfoItem; opacity: spreadMaths.tileInfoOpacity; visible: spreadMaths.itemVisible }
1074+ },
1075+ State {
1076+ name: "stagedRightEdge"
1077+ when: (root.mode == "staged" || root.mode == "stagedWithSideStage") && (root.state == "sideStagedRightEdge" || root.state == "stagedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running)
1078+ PropertyChanges {
1079+ target: stagedRightEdgeMaths
1080+ progress: Math.max(edgeBarrier.progress, rightEdgeDragArea.draggedProgress)
1081+ }
1082+ PropertyChanges {
1083+ target: appDelegate
1084+ x: stagedRightEdgeMaths.animatedX
1085+ y: stagedRightEdgeMaths.animatedY
1086+ z: stagedRightEdgeMaths.animatedZ
1087+ height: stagedRightEdgeMaths.animatedHeight
1088+ requestedWidth: decoratedWindow.oldRequestedWidth
1089+ requestedHeight: decoratedWindow.oldRequestedHeight
1090+ visible: appDelegate.x < root.width
1091+ }
1092+ PropertyChanges {
1093+ target: decoratedWindow
1094+ hasDecoration: false
1095+ angle: stagedRightEdgeMaths.animatedAngle
1096+ itemScale: stagedRightEdgeMaths.animatedScale
1097+ scaleToPreviewSize: spreadItem.stackHeight
1098+ scaleToPreviewProgress: stagedRightEdgeMaths.scaleToPreviewProgress
1099+ shadowOpacity: .3
1100+ }
1101+ },
1102+ State {
1103+ name: "windowedRightEdge"
1104+ when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running || edgeBarrier.progress > 0)
1105+ PropertyChanges {
1106+ target: windowedRightEdgeMaths
1107+ progress: Math.max(rightEdgeDragArea.progress, edgeBarrier.progress)
1108+ }
1109+ PropertyChanges {
1110+ target: appDelegate
1111+ x: windowedRightEdgeMaths.animatedX
1112+ y: windowedRightEdgeMaths.animatedY
1113+ z: windowedRightEdgeMaths.animatedZ
1114+ height: stagedRightEdgeMaths.animatedHeight
1115+ requestedWidth: decoratedWindow.oldRequestedWidth
1116+ requestedHeight: decoratedWindow.oldRequestedHeight
1117+ }
1118+ PropertyChanges {
1119+ target: decoratedWindow
1120+ showDecoration: windowedRightEdgeMaths.decorationHeight
1121+ angle: windowedRightEdgeMaths.animatedAngle
1122+ itemScale: windowedRightEdgeMaths.animatedScale
1123+ scaleToPreviewSize: spreadItem.stackHeight
1124+ scaleToPreviewProgress: windowedRightEdgeMaths.scaleToPreviewProgress
1125+ shadowOpacity: .3
1126+ }
1127+ PropertyChanges {
1128+ target: opacityEffect;
1129+ opacityValue: windowedRightEdgeMaths.opacityMask
1130+ sourceItem: windowedRightEdgeMaths.opacityMask < 1 ? decoratedWindow : null
1131+ }
1132+ },
1133+ State {
1134+ name: "staged"; when: root.state == "staged"
1135+ PropertyChanges {
1136+ target: appDelegate
1137+ x: stageMaths.itemX
1138+ y: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1139+ requestedWidth: appContainer.width
1140+ requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - PanelState.panelHeight
1141+ visuallyMaximized: true
1142+ visible: appDelegate.x < root.width
1143+ }
1144+ PropertyChanges {
1145+ target: decoratedWindow
1146+ hasDecoration: false
1147+ }
1148+ PropertyChanges {
1149+ target: resizeArea
1150+ enabled: false
1151+ }
1152+ PropertyChanges {
1153+ target: stageMaths
1154+ animateX: !focusAnimation.running && itemIndex !== spreadItem.highlightedIndex
1155+ }
1156+ },
1157+ State {
1158+ name: "stagedWithSideStage"; when: root.state == "stagedWithSideStage"
1159+ PropertyChanges {
1160+ target: stageMaths
1161+ itemIndex: index
1162+ }
1163+ PropertyChanges {
1164+ target: appDelegate
1165+ x: stageMaths.itemX
1166+ y: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1167+ z: stageMaths.itemZ
1168+ requestedWidth: stageMaths.itemWidth
1169+ requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - PanelState.panelHeight
1170+ visuallyMaximized: true
1171+ visible: appDelegate.x < root.width
1172+ }
1173+ PropertyChanges {
1174+ target: decoratedWindow
1175+ hasDecoration: false
1176+ }
1177+ PropertyChanges {
1178+ target: resizeArea
1179+ enabled: false
1180+ }
1181+ },
1182+ State {
1183+ name: "maximized"; when: appDelegate.windowState == WindowStateStorage.WindowStateMaximized
1184+ PropertyChanges {
1185+ target: appDelegate;
1186+ requestedX: root.leftMargin;
1187+ requestedY: 0;
1188+ visuallyMinimized: false;
1189+ visuallyMaximized: true
1190+ requestedWidth: appContainer.width - root.leftMargin;
1191+ requestedHeight: appContainer.height;
1192+ }
1193+ PropertyChanges { target: touchControls; enabled: true }
1194+ },
1195+ State {
1196+ name: "fullscreen"; when: appDelegate.fullscreen && !appDelegate.minimized
1197+ PropertyChanges {
1198+ target: appDelegate;
1199+ requestedX: 0
1200+ requestedY: 0
1201+ requestedWidth: appContainer.width;
1202+ requestedHeight: appContainer.height;
1203+ }
1204+ PropertyChanges { target: decoratedWindow; hasDecoration: false }
1205+ },
1206+ State {
1207+ name: "normal";
1208+ when: appDelegate.windowState == WindowStateStorage.WindowStateNormal
1209+ PropertyChanges {
1210+ target: appDelegate
1211+ visuallyMinimized: false
1212+ visuallyMaximized: false
1213+ }
1214+ PropertyChanges { target: touchControls; enabled: true }
1215+ PropertyChanges { target: resizeArea; enabled: true }
1216+ PropertyChanges { target: decoratedWindow; shadowOpacity: .3}
1217+ },
1218+ State {
1219+ name: "restored";
1220+ when: appDelegate.windowState == WindowStateStorage.WindowStateRestored
1221+ extend: "normal"
1222+ PropertyChanges {
1223+ target: appDelegate;
1224+ windowedX: restoredX;
1225+ windowedY: restoredY;
1226+ }
1227+ },
1228+ State {
1229+ name: "semiMaximized"
1230+ PropertyChanges { target: touchControls; enabled: true }
1231+ PropertyChanges { target: resizeArea; enabled: true }
1232+ PropertyChanges { target: decoratedWindow; shadowOpacity: .3 }
1233+ },
1234+ State {
1235+ name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
1236+ extend: "semiMaximized"
1237+ PropertyChanges {
1238+ target: appDelegate
1239+ windowedX: root.leftMargin
1240+ windowedY: PanelState.panelHeight
1241+ windowedWidth: (appContainer.width - root.leftMargin)/2
1242+ windowedHeight: appContainer.height - PanelState.panelHeight
1243+ }
1244+ },
1245+ State {
1246+ name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
1247+ extend: "maximizedLeft"
1248+ PropertyChanges {
1249+ target: appDelegate;
1250+ windowedX: (appContainer.width + root.leftMargin)/2
1251+ }
1252+ },
1253+ State {
1254+ name: "maximizedTopLeft"; when: appDelegate.maximizedTopLeft && !appDelegate.minimized
1255+ extend: "semiMaximized"
1256+ PropertyChanges {
1257+ target: appDelegate
1258+ windowedX: root.leftMargin
1259+ windowedY: PanelState.panelHeight
1260+ windowedWidth: (appContainer.width - root.leftMargin)/2
1261+ windowedHeight: (appContainer.height - PanelState.panelHeight)/2
1262+ }
1263+ },
1264+ State {
1265+ name: "maximizedTopRight"; when: appDelegate.maximizedTopRight && !appDelegate.minimized
1266+ extend: "maximizedTopLeft"
1267+ PropertyChanges {
1268+ target: appDelegate
1269+ windowedX: (appContainer.width + root.leftMargin)/2
1270+ }
1271+ },
1272+ State {
1273+ name: "maximizedBottomLeft"; when: appDelegate.maximizedBottomLeft && !appDelegate.minimized
1274+ extend: "semiMaximized"
1275+ PropertyChanges {
1276+ target: appDelegate
1277+ windowedX: root.leftMargin
1278+ windowedY: (appContainer.height + PanelState.panelHeight)/2
1279+ windowedWidth: (appContainer.width - root.leftMargin)/2
1280+ windowedHeight: appContainer.height/2
1281+ }
1282+ },
1283+ State {
1284+ name: "maximizedBottomRight"; when: appDelegate.maximizedBottomRight && !appDelegate.minimized
1285+ extend: "maximizedBottomLeft"
1286+ PropertyChanges {
1287+ target: appDelegate
1288+ windowedX: (appContainer.width + root.leftMargin)/2
1289+ }
1290+ },
1291+ State {
1292+ name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
1293+ extend: "semiMaximized"
1294+ PropertyChanges { target: appDelegate; requestedX: root.leftMargin; requestedY: windowedY;
1295+ requestedWidth: appContainer.width - root.leftMargin; requestedHeight: appDelegate.windowedHeight }
1296+ },
1297+ State {
1298+ name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
1299+ extend: "semiMaximized"
1300+ PropertyChanges { target: appDelegate; requestedX: windowedX; requestedY: PanelState.panelHeight;
1301+ requestedWidth: appDelegate.windowedWidth; requestedHeight: appContainer.height - PanelState.panelHeight }
1302+ },
1303+ State {
1304+ name: "minimized"; when: appDelegate.minimized
1305+ PropertyChanges {
1306+ target: appDelegate
1307+ requestedX: -appDelegate.width / 2
1308+ scale: units.gu(5) / appDelegate.width
1309+ opacity: 0;
1310+ visuallyMinimized: true
1311+ visuallyMaximized: false
1312+ }
1313+ }
1314+ ]
1315+ transitions: [
1316+ Transition {
1317+ from: "staged,stagedWithSideStage"; to: "normal"
1318+ enabled: appDelegate.animationsEnabled
1319+ PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
1320+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,opacity,requestedWidth,requestedHeight,scale"; duration: priv.animationDuration }
1321+ },
1322+ Transition {
1323+ from: "normal,maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight";
1324+ to: "staged,stagedWithSideStage"
1325+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,requestedWidth,requestedHeight"; duration: priv.animationDuration}
1326+ },
1327+ Transition {
1328+ from: "maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight,minimized";
1329+ to: "normal,restored"
1330+ enabled: appDelegate.animationsEnabled
1331+ PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
1332+ UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,windowedX,windowedY,requestedWidth,requestedHeight,windowedWidth,windowedHeight,scale";
1333+ duration: priv.animationDuration }
1334+ },
1335+ Transition {
1336+ to: "minimized"
1337+ enabled: appDelegate.animationsEnabled
1338+ PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1339+ SequentialAnimation {
1340+ UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
1341+ PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1342+ ScriptAction {
1343+ script: {
1344+ if (appDelegate.minimized) {
1345+ appDelegate.focus = false;
1346+ priv.focusNext();
1347+ }
1348+ }
1349+ }
1350+ }
1351+ },
1352+ Transition {
1353+ to: "spread"
1354+ // DecoratedWindow wants the scaleToPreviewSize set before enabling scaleToPreview
1355+ PropertyAction { target: appDelegate; properties: "z,visible" }
1356+ PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
1357+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,height"; duration: priv.animationDuration }
1358+ UbuntuNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle,scaleToPreviewProgress"; duration: priv.animationDuration }
1359+ },
1360+ Transition {
1361+ from: "normal,staged"; to: "stagedWithSideStage"
1362+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y"; duration: priv.animationDuration }
1363+ UbuntuNumberAnimation { target: appDelegate; properties: "requestedWidth,requestedHeight"; duration: priv.animationDuration }
1364+ },
1365+ Transition {
1366+ to: "windowedRightEdge"
1367+ ScriptAction {
1368+ script: {
1369+ windowedRightEdgeMaths.startX = appDelegate.requestedX
1370+ windowedRightEdgeMaths.startY = appDelegate.requestedY
1371+
1372+ if (index == 1) {
1373+ var thisRect = { x: appDelegate.windowedX, y: appDelegate.windowedY, width: appDelegate.requestedWidth, height: appDelegate.requestedHeight }
1374+ var otherDelegate = appRepeater.itemAt(0);
1375+ var otherRect = { x: otherDelegate.windowedX, y: otherDelegate.windowedY, width: otherDelegate.requestedWidth, height: otherDelegate.requestedHeight }
1376+ var intersectionRect = MathUtils.intersectionRect(thisRect, otherRect)
1377+ var mappedInterSectionRect = appDelegate.mapFromItem(root, intersectionRect.x, intersectionRect.y)
1378+ opacityEffect.maskX = mappedInterSectionRect.x
1379+ opacityEffect.maskY = mappedInterSectionRect.y
1380+ opacityEffect.maskWidth = intersectionRect.width
1381+ opacityEffect.maskHeight = intersectionRect.height
1382+ }
1383+ }
1384+ }
1385+ },
1386+ Transition {
1387+ from: "stagedRightEdge"; to: "staged"
1388+ enabled: rightEdgeDragArea.cancelled // only transition back to state if the gesture was cancelled, in the other cases we play the focusAnimations.
1389+ SequentialAnimation {
1390+ ParallelAnimation {
1391+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,height,width,scale"; duration: priv.animationDuration }
1392+ UbuntuNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle,scaleToPreviewProgress"; duration: priv.animationDuration }
1393+ }
1394+ // We need to release scaleToPreviewSize at last
1395+ PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
1396+ PropertyAction { target: appDelegate; property: "visible" }
1397+ }
1398+ },
1399+ Transition {
1400+ from: "normal,maximized,maximizedLeft,maximizedRight,maximizedTop,maximizedBottom,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1401+ to: "normal,maximized,maximizedLeft,maximizedRight,maximizedTop,maximizedBottom,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1402+ enabled: appDelegate.animationsEnabled
1403+ SequentialAnimation {
1404+ PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1405+ UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,windowedX,windowedY,opacity,scale,requestedWidth,requestedHeight,windowedWidth,windowedHeight";
1406+ duration: priv.animationDuration }
1407+ PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1408+ ScriptAction { script: { fakeRectangle.stop(); } }
1409+ }
1410+ }
1411+ ]
1412+
1413+ Binding {
1414+ target: PanelState
1415+ property: "decorationsAlwaysVisible"
1416+ value: appDelegate && appDelegate.maximized && touchControls.overlayShown
1417+ }
1418+
1419+ WindowResizeArea {
1420+ id: resizeArea
1421+ objectName: "windowResizeArea"
1422+
1423+ // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
1424+ anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
1425+
1426+ target: appDelegate
1427+ minWidth: units.gu(10)
1428+ minHeight: units.gu(10)
1429+ borderThickness: units.gu(2)
1430+ windowId: model.application.appId // FIXME: Change this to point to windowId once we have such a thing
1431+ screenWidth: appContainer.width
1432+ screenHeight: appContainer.height
1433+ leftMargin: root.leftMargin
1434+ enabled: false
1435+ visible: enabled
1436+
1437+ onPressed: {
1438+ appDelegate.focus = true;
1439+ }
1440+
1441+ Component.onDestruction: {
1442+ saveWindowState();
1443+ }
1444+ }
1445+
1446+ DecoratedWindow {
1447+ id: decoratedWindow
1448+ objectName: "decoratedWindow"
1449+ anchors.left: appDelegate.left
1450+ anchors.top: appDelegate.top
1451+ application: model.application
1452+ surface: model.surface
1453+ active: appDelegate.focus
1454+ focus: true
1455+ interactive: root.interactive
1456+ showDecoration: 1
1457+ maximizeButtonShown: appDelegate.canBeMaximized
1458+ overlayShown: touchControls.overlayShown
1459+ width: implicitWidth
1460+ height: implicitHeight
1461+ highlightSize: windowInfoItem.iconMargin / 2
1462+ stageWidth: appContainer.width
1463+ stageHeight: appContainer.height
1464+
1465+ requestedWidth: appDelegate.requestedWidth
1466+ requestedHeight: appDelegate.requestedHeight
1467+
1468+ property int oldRequestedWidth: -1
1469+ property int oldRequestedHeight: -1
1470+
1471+ onRequestedWidthChanged: oldRequestedWidth = requestedWidth
1472+ onRequestedHeightChanged: oldRequestedHeight = requestedHeight
1473+
1474+ onCloseClicked: { appDelegate.close(); }
1475+ onMaximizeClicked: appDelegate.anyMaximized ? appDelegate.restoreFromMaximized() : appDelegate.maximize();
1476+ onMaximizeHorizontallyClicked: appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
1477+ onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
1478+ onMinimizeClicked: appDelegate.minimize()
1479+ onDecorationPressed: { appDelegate.focus = true; }
1480+ onDecorationReleased: fakeRectangle.commit();
1481+
1482+ property real angle: 0
1483+ property real itemScale: 1
1484+ transform: [
1485+ Scale {
1486+ origin.x: 0
1487+ origin.y: decoratedWindow.implicitHeight / 2
1488+ xScale: decoratedWindow.itemScale
1489+ yScale: decoratedWindow.itemScale
1490+ },
1491+ Rotation {
1492+ origin { x: 0; y: (decoratedWindow.height / 2) }
1493+ axis { x: 0; y: 1; z: 0 }
1494+ angle: decoratedWindow.angle
1495+ }
1496+ ]
1497+ }
1498+
1499+ OpacityMask {
1500+ id: opacityEffect
1501+ anchors.fill: decoratedWindow
1502+ }
1503+
1504+ WindowControlsOverlay {
1505+ id: touchControls
1506+ target: appDelegate
1507+ enabled: false
1508+ visible: enabled
1509+ stageWidth: appContainer.width
1510+ stageHeight: appContainer.height
1511+
1512+ onFakeMaximizeAnimationRequested: if (!appDelegate.maximized) fakeRectangle.maximize(amount, true)
1513+ onFakeMaximizeLeftAnimationRequested: if (!appDelegate.maximizedLeft) fakeRectangle.maximizeLeft(amount, true)
1514+ onFakeMaximizeRightAnimationRequested: if (!appDelegate.maximizedRight) fakeRectangle.maximizeRight(amount, true)
1515+ onFakeMaximizeTopLeftAnimationRequested: if (!appDelegate.maximizedTopLeft) fakeRectangle.maximizeTopLeft(amount, true);
1516+ onFakeMaximizeTopRightAnimationRequested: if (!appDelegate.maximizedTopRight) fakeRectangle.maximizeTopRight(amount, true);
1517+ onFakeMaximizeBottomLeftAnimationRequested: if (!appDelegate.maximizedBottomLeft) fakeRectangle.maximizeBottomLeft(amount, true);
1518+ onFakeMaximizeBottomRightAnimationRequested: if (!appDelegate.maximizedBottomRight) fakeRectangle.maximizeBottomRight(amount, true);
1519+ onStopFakeAnimation: fakeRectangle.stop();
1520+ onDragReleased: fakeRectangle.commit();
1521+ }
1522+
1523+ WindowedFullscreenPolicy {
1524+ id: windowedFullscreenPolicy
1525+ active: root.mode == "windowed"
1526+ surface: model.surface
1527+ }
1528+ StagedFullscreenPolicy {
1529+ id: stagedFullscreenPolicy
1530+ active: root.mode == "staged" || root.mode == "stagedWithSideStage"
1531+ surface: model.surface
1532+ }
1533+
1534+ SpreadDelegateInputArea {
1535+ id: dragArea
1536+ objectName: "dragArea"
1537+ anchors.fill: decoratedWindow
1538+ enabled: false
1539+ closeable: !appDelegate.isDash
1540+
1541+ onClicked: {
1542+ spreadItem.highlightedIndex = index;
1543+ if (distance == 0) {
1544+ priv.goneToSpread = false;
1545+ }
1546+ }
1547+ onClose: {
1548+ priv.closingIndex = index
1549+ if (model.surface) { // could be stopped by OOM
1550+ model.surface.close()
1551+ } else if (model.application) {
1552+ root.applicationManager.stopApplication(model.application.appId);
1553+ }
1554+ }
1555+ }
1556+
1557+ WindowInfoItem {
1558+ id: windowInfoItem
1559+ objectName: "windowInfoItem"
1560+ anchors { left: parent.left; top: decoratedWindow.bottom; topMargin: units.gu(1) }
1561+ title: model.application.name
1562+ iconSource: model.application.icon
1563+ height: spreadItem.appInfoHeight
1564+ opacity: 0
1565+ z: 1
1566+ visible: opacity > 0
1567+ maxWidth: {
1568+ var nextApp = appRepeater.itemAt(index + 1);
1569+ if (nextApp) {
1570+ return nextApp.x - appDelegate.x - units.gu(1)
1571+ }
1572+ return appDelegate.width;
1573+ }
1574+
1575+ onClicked: {
1576+ spreadItem.highlightedIndex = index;
1577+ priv.goneToSpread = false;
1578+ }
1579+ }
1580+
1581+ Image {
1582+ id: closeImage
1583+ anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset }
1584+ source: "graphics/window-close.svg"
1585+ readonly property var mousePos: hoverMouseArea.mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1586+ visible: !appDelegate.isDash
1587+ && index == spreadItem.highlightedIndex
1588+ && mousePos.y < (decoratedWindow.height / 3)
1589+ && mousePos.y > -units.gu(4)
1590+ && mousePos.x > -units.gu(4)
1591+ && mousePos.x < (decoratedWindow.width * 2 / 3)
1592+ height: units.gu(2)
1593+ width: height
1594+ sourceSize.width: width
1595+ sourceSize.height: height
1596+
1597+ MouseArea {
1598+ id: closeMouseArea
1599+ objectName: "closeMouseArea"
1600+ anchors.fill: closeImage
1601+ anchors.margins: -units.gu(2)
1602+ onClicked: {
1603+ priv.closingIndex = index;
1604+ appDelegate.close();
1605+ }
1606+ }
1607+ }
1608+ }
1609+ }
1610+ }
1611+
1612+ FakeMaximizeDelegate {
1613+ id: fakeRectangle
1614+ target: priv.focusedAppDelegate
1615+ leftMargin: root.leftMargin
1616+ appContainerWidth: appContainer.width
1617+ appContainerHeight: appContainer.height
1618+ }
1619+
1620+ EdgeBarrier {
1621+ id: edgeBarrier
1622+
1623+ // NB: it does its own positioning according to the specified edge
1624+ edge: Qt.RightEdge
1625+
1626+ onPassed: priv.goneToSpread = true;
1627+
1628+ material: Component {
1629+ Item {
1630+ Rectangle {
1631+ width: parent.height
1632+ height: parent.width
1633+ rotation: 90
1634+ anchors.centerIn: parent
1635+ gradient: Gradient {
1636+ GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
1637+ GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
1638+ }
1639+ }
1640+ }
1641+ }
1642+ }
1643+
1644+ MouseArea {
1645+ id: hoverMouseArea
1646+ objectName: "hoverMouseArea"
1647+ anchors.fill: appContainer
1648+ propagateComposedEvents: true
1649+ hoverEnabled: true
1650+ enabled: false
1651+ visible: enabled
1652+
1653+ property int scrollAreaWidth: width / 3
1654+ property bool progressiveScrollingEnabled: false
1655+
1656+ onMouseXChanged: {
1657+ mouse.accepted = false
1658+
1659+ if (hoverMouseArea.pressed) {
1660+ return;
1661+ }
1662+
1663+ // Find the hovered item and mark it active
1664+ var mapped = mapToItem(appContainer, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1665+ var itemUnder = appContainer.childAt(mapped.x, mapped.y)
1666+ if (itemUnder) {
1667+ mapped = mapToItem(itemUnder, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1668+ var delegateChild = itemUnder.childAt(mapped.x, mapped.y)
1669+ if (delegateChild && (delegateChild.objectName === "dragArea" || delegateChild.objectName === "windowInfoItem")) {
1670+ spreadItem.highlightedIndex = appRepeater.indexOf(itemUnder)
1671+ }
1672+ }
1673+
1674+ if (floatingFlickable.contentWidth > floatingFlickable.width) {
1675+ var margins = floatingFlickable.width * 0.05;
1676+
1677+ if (!progressiveScrollingEnabled && mouseX < floatingFlickable.width - scrollAreaWidth) {
1678+ progressiveScrollingEnabled = true
1679+ }
1680+
1681+ // do we need to scroll?
1682+ if (mouseX < scrollAreaWidth + margins) {
1683+ var progress = Math.min(1, (scrollAreaWidth + margins - mouseX) / (scrollAreaWidth - margins));
1684+ var contentX = (1 - progress) * (floatingFlickable.contentWidth - floatingFlickable.width)
1685+ floatingFlickable.contentX = Math.max(0, Math.min(floatingFlickable.contentX, contentX))
1686+ }
1687+ if (mouseX > floatingFlickable.width - scrollAreaWidth && progressiveScrollingEnabled) {
1688+ var progress = Math.min(1, (mouseX - (floatingFlickable.width - scrollAreaWidth)) / (scrollAreaWidth - margins))
1689+ var contentX = progress * (floatingFlickable.contentWidth - floatingFlickable.width)
1690+ floatingFlickable.contentX = Math.min(floatingFlickable.contentWidth - floatingFlickable.width, Math.max(floatingFlickable.contentX, contentX))
1691+ }
1692+ }
1693+ }
1694+
1695+ onPressed: mouse.accepted = false
1696+ }
1697+
1698+ FloatingFlickable {
1699+ id: floatingFlickable
1700+ objectName: "spreadFlickable"
1701+ anchors.fill: appContainer
1702+ enabled: false
1703+ contentWidth: spreadItem.spreadTotalWidth
1704+
1705+ function snap(toIndex) {
1706+ var delegate = appRepeater.itemAt(toIndex)
1707+ var targetContentX = floatingFlickable.contentWidth / spreadItem.totalItemCount * toIndex;
1708+ if (targetContentX - floatingFlickable.contentX > spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) {
1709+ var offset = (spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) - (targetContentX - floatingFlickable.contentX)
1710+ snapAnimation.to = floatingFlickable.contentX - offset;
1711+ snapAnimation.start();
1712+ } else if (targetContentX - floatingFlickable.contentX < spreadItem.leftStackXPos + units.gu(1)) {
1713+ var offset = (spreadItem.leftStackXPos + units.gu(1)) - (targetContentX - floatingFlickable.contentX);
1714+ snapAnimation.to = floatingFlickable.contentX - offset;
1715+ snapAnimation.start();
1716+ }
1717+ }
1718+ UbuntuNumberAnimation {id: snapAnimation; target: floatingFlickable; property: "contentX"}
1719+ }
1720+
1721+ PropertyAnimation {
1722+ id: shortRightEdgeSwipeAnimation
1723+ property: "x"
1724+ to: 0
1725+ duration: priv.animationDuration
1726+ }
1727+
1728+ SwipeArea {
1729+ id: rightEdgeDragArea
1730+ objectName: "rightEdgeDragArea"
1731+ direction: Direction.Leftwards
1732+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
1733+ width: root.dragAreaWidth
1734+
1735+ property var gesturePoints: []
1736+ property bool cancelled: false
1737+
1738+ property real progress: dragging ? -touchPosition.x / root.width : 0
1739+ onProgressChanged: {
1740+ if (dragging) {
1741+ draggedProgress = progress;
1742+ }
1743+ }
1744+
1745+ property real draggedProgress: 0
1746+
1747+ onTouchPositionChanged: {
1748+ gesturePoints.push(touchPosition.x);
1749+ if (gesturePoints.length > 10) {
1750+ gesturePoints.splice(0, gesturePoints.length - 10)
1751+ }
1752+ }
1753+
1754+ onDraggingChanged: {
1755+ if (dragging) {
1756+ // A potential edge-drag gesture has started. Start recording it
1757+ gesturePoints = [];
1758+ cancelled = false;
1759+ draggedProgress = 0;
1760+ } else {
1761+ // Ok. The user released. Did he drag far enough to go to full spread?
1762+ if (gesturePoints[gesturePoints.length - 1] < -spreadItem.rightEdgeBreakPoint * spreadItem.width ) {
1763+
1764+ // He dragged far enough, but if the last movement was a flick to the right again, he wants to cancel the spread again.
1765+ var oneWayFlickToRight = true;
1766+ var smallestX = gesturePoints[0]-1;
1767+ for (var i = 0; i < gesturePoints.length; i++) {
1768+ if (gesturePoints[i] <= smallestX) {
1769+ oneWayFlickToRight = false;
1770+ break;
1771+ }
1772+ smallestX = gesturePoints[i];
1773+ }
1774+
1775+ if (!oneWayFlickToRight) {
1776+ // Ok, the user made it, let's go to spread!
1777+ priv.goneToSpread = true;
1778+ } else {
1779+ cancelled = true;
1780+ }
1781+ } else {
1782+ // Ok, the user didn't drag far enough to cross the breakPoint
1783+ // Find out if it was a one-way movement to the left, in which case we just switch directly to next app.
1784+ var oneWayFlick = true;
1785+ var smallestX = rightEdgeDragArea.width;
1786+ for (var i = 0; i < gesturePoints.length; i++) {
1787+ if (gesturePoints[i] >= smallestX) {
1788+ oneWayFlick = false;
1789+ break;
1790+ }
1791+ smallestX = gesturePoints[i];
1792+ }
1793+
1794+ if (appRepeater.count > 1 &&
1795+ (oneWayFlick && rightEdgeDragArea.distance > units.gu(2) || rightEdgeDragArea.distance > spreadItem.rightEdgeBreakPoint * spreadItem.width)) {
1796+ var nextStage = appRepeater.itemAt(priv.nextInStack).stage
1797+ for (var i = 0; i < appRepeater.count; i++) {
1798+ if (i != priv.nextInStack && appRepeater.itemAt(i).stage == nextStage) {
1799+ appRepeater.itemAt(i).playHidingAnimation()
1800+ break;
1801+ }
1802+ }
1803+ appRepeater.itemAt(priv.nextInStack).playFocusAnimation()
1804+ if (appRepeater.itemAt(priv.nextInStack).stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
1805+ sideStage.show();
1806+ }
1807+
1808+ } else {
1809+ cancelled = true;
1810+ }
1811+
1812+ gesturePoints = [];
1813+ }
1814+ }
1815+ }
1816+ }
1817+
1818+ TabletSideStageTouchGesture {
1819+ id: triGestureArea
1820+ objectName: "triGestureArea"
1821+ anchors.fill: parent
1822+ enabled: false
1823+ property Item appDelegate
1824+
1825+ dragComponent: dragComponent
1826+ dragComponentProperties: { "appDelegate": appDelegate }
1827+
1828+ onPressed: {
1829+ function matchDelegate(obj) { return String(obj.objectName).indexOf("appDelegate") >= 0; }
1830+
1831+ var delegateAtCenter = Functions.itemAt(appContainer, x, y, matchDelegate);
1832+ if (!delegateAtCenter) return;
1833+
1834+ appDelegate = delegateAtCenter;
1835+ }
1836+
1837+ onClicked: {
1838+ if (sideStage.shown) {
1839+ sideStage.hide();
1840+ } else {
1841+ sideStage.show();
1842+ priv.updateMainAndSideStageIndexes()
1843+ }
1844+ }
1845+
1846+ onDragStarted: {
1847+ // If we're dragging to the sidestage.
1848+ if (!sideStage.shown) {
1849+ sideStage.show();
1850+ }
1851+ }
1852+
1853+ Component {
1854+ id: dragComponent
1855+ SurfaceContainer {
1856+ property Item appDelegate
1857+
1858+ surface: appDelegate ? appDelegate.surface : null
1859+
1860+ consumesInput: false
1861+ interactive: false
1862+ focus: false
1863+ requestedWidth: appDelegate.requestedWidth
1864+ requestedHeight: appDelegate.requestedHeight
1865+
1866+ width: units.gu(40)
1867+ height: units.gu(40)
1868+
1869+ Drag.hotSpot.x: width/2
1870+ Drag.hotSpot.y: height/2
1871+ // only accept opposite stage.
1872+ Drag.keys: {
1873+ if (!surface) return "Disabled";
1874+ if (appDelegate.isDash) return "Disabled";
1875+
1876+ if (appDelegate.stage === ApplicationInfo.MainStage) {
1877+ if (appDelegate.application.supportedOrientations
1878+ & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
1879+ return "MainStage";
1880+ }
1881+ return "Disabled";
1882+ }
1883+ return "SideStage";
1884+ }
1885+ }
1886+ }
1887+ }
1888+}
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
@@ -109,7 +109,7 @@ MouseArea {
109109 onMaximizeClicked: root.maximizeClicked();
110110 onMaximizeHorizontallyClicked: if (root.target.canBeMaximizedHorizontally) root.maximizeHorizontallyClicked();
111111 onMaximizeVerticallyClicked: if (root.target.canBeMaximizedVertically) root.maximizeVerticallyClicked();
112− closeButtonShown: root.target.application.appId !== "unity8-dash"
112+ closeButtonShown: root.target.appId !== "unity8-dash"
113113 }
114114
115115 Item {
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−196
@@ -1,196 +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 Unity.ApplicationMenu 0.1
21−import Unity.Indicators 0.1 as Indicators
22−import "../Components/PanelState"
23−
24−FocusScope {
25− id: root
26−
27− width: !counterRotate ? applicationWindow.width : applicationWindow.height
28− height: visibleDecorationHeight + (!counterRotate ? applicationWindow.height : applicationWindow.width)
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 fullscreen: applicationWindow.fullscreen
36− property alias maximizeButtonShown: decoration.maximizeButtonShown
37−
38− readonly property bool decorationShown: !fullscreen
39− property bool highlightShown: false
40− property real shadowOpacity: 1
41−
42− property real requestedWidth
43− property real requestedHeight
44−
45− property alias surfaceOrientationAngle: applicationWindow.surfaceOrientationAngle
46− readonly property real visibleDecorationHeight: root.decorationShown ? decoration.height : 0
47− readonly property bool counterRotate: surfaceOrientationAngle != 0 && surfaceOrientationAngle != 180
48−
49− readonly property int minimumWidth: !counterRotate ? applicationWindow.minimumWidth : applicationWindow.minimumHeight
50− readonly property int minimumHeight: visibleDecorationHeight + (!counterRotate ? applicationWindow.minimumHeight : applicationWindow.minimumWidth)
51− readonly property int maximumWidth: !counterRotate ? applicationWindow.maximumWidth : applicationWindow.maximumHeight
52− readonly property int maximumHeight: (root.decorationShown && applicationWindow.maximumHeight > 0 ? decoration.height : 0)
53− + (!counterRotate ? applicationWindow.maximumHeight : applicationWindow.maximumWidth)
54− readonly property int widthIncrement: !counterRotate ? applicationWindow.widthIncrement : applicationWindow.heightIncrement
55− readonly property int heightIncrement: !counterRotate ? applicationWindow.heightIncrement : applicationWindow.widthIncrement
56−
57− property alias overlayShown: decoration.overlayShown
58− property alias stageWidth: moveHandler.stageWidth
59− property alias stageHeight: moveHandler.stageHeight
60− readonly property alias dragging: moveHandler.dragging
61−
62− readonly property Item clientAreaItem: applicationWindow
63−
64− signal closeClicked()
65− signal maximizeClicked()
66− signal maximizeHorizontallyClicked()
67− signal maximizeVerticallyClicked()
68− signal minimizeClicked()
69− signal decorationPressed()
70− signal decorationReleased()
71−
72− Rectangle {
73− id: selectionHighlight
74− anchors.fill: parent
75− anchors.margins: -units.gu(1)
76− color: "white"
77− opacity: highlightShown ? 0.15 : 0
78− }
79−
80− Rectangle {
81− anchors { left: selectionHighlight.left; right: selectionHighlight.right; bottom: selectionHighlight.bottom; }
82− height: units.dp(2)
83− color: theme.palette.normal.focus
84− visible: highlightShown
85− }
86−
87− BorderImage {
88− anchors {
89− fill: root
90− margins: active ? -units.gu(2) : -units.gu(1.5)
91− }
92− source: "graphics/dropshadow2gu.sci"
93− opacity: root.shadowOpacity * .3
94− visible: !fullscreen
95− }
96−
97− ApplicationWindow {
98− id: applicationWindow
99− objectName: "appWindow"
100− anchors.top: parent.top
101− anchors.topMargin: decoration.height
102− anchors.left: parent.left
103− readonly property real requestedHeightMinusDecoration: root.requestedHeight - root.visibleDecorationHeight
104− requestedHeight: !counterRotate ? requestedHeightMinusDecoration : root.requestedWidth
105− requestedWidth: !counterRotate ? root.requestedWidth : requestedHeightMinusDecoration
106− interactive: true
107− focus: true
108−
109− transform: Rotation {
110− readonly property int rotationAngle: applicationWindow.application &&
111− applicationWindow.application.rotatesWindowContents
112− ? ((360 - applicationWindow.surfaceOrientationAngle) % 360) : 0
113− origin.x: {
114− if (rotationAngle == 90) return applicationWindow.height / 2;
115− else if (rotationAngle == 270) return applicationWindow.width / 2;
116− else if (rotationAngle == 180) return applicationWindow.width / 2;
117− else return 0;
118− }
119− origin.y: {
120− if (rotationAngle == 90) return applicationWindow.height / 2;
121− else if (rotationAngle == 270) return applicationWindow.width / 2;
122− else if (rotationAngle == 180) return applicationWindow.height / 2;
123− else return 0;
124− }
125− angle: rotationAngle
126− }
127− }
128−
129− MouseArea {
130− anchors { left: parent.left; top: parent.top; right: parent.right }
131− height: units.gu(3)
132−
133− drag.target: Item {}
134− drag.filterChildren: true
135− drag.threshold: 0
136−
137− onPressed: root.decorationPressed();
138− onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
139− onPositionChanged: moveHandler.handlePositionChanged(mouse)
140− onReleased: {
141− root.decorationReleased();
142− moveHandler.handleReleased();
143− }
144−
145− WindowDecoration {
146− id: decoration
147− target: root.parent
148− objectName: "appWindowDecoration"
149− anchors.fill: parent
150− title: applicationWindow.title
151− visible: root.decorationShown
152−
153− onCloseClicked: root.closeClicked();
154− onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); }
155− onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); }
156− onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }
157− onMinimizeClicked: root.minimizeClicked();
158−
159− enableMenus: active &&
160− (PanelState.focusedPersistentSurfaceId !== surface.persistentId ||
161− (PanelState.focusedPersistentSurfaceId === surface.persistentId && !PanelState.focusedSurfaceMaximized))
162− menu: sharedAppModel.model
163−
164− Indicators.SharedUnityMenuModel {
165− id: sharedAppModel
166− property var menus: surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : []
167− property var menuService: menus.length > 0 ? menus[0] : null
168−
169− busName: menuService ? menuService.service : ""
170− menuObjectPath: menuService && menuService.menuPath ? menuService.menuPath : ""
171− actions: menuService && menuService.actionPath ? { "unity": menuService.actionPath } : {}
172− }
173−
174− Connections {
175− target: ApplicationMenuRegistry
176− onSurfaceMenuRegistered: {
177− if (surface && surfaceId === surface.persistentId) {
178− sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] });
179− }
180− }
181− onSurfaceMenuUnregistered: {
182− if (surface && surfaceId === surface.persistentId) {
183− sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] });
184− }
185− }
186− }
187− }
188− }
189−
190− MoveHandler {
191− id: moveHandler
192− objectName: "moveHandler"
193− target: root.parent
194− buttonsWidth: decoration.buttonsWidth
195− }
196−}
qml/Stages/DesktopStage.qmldeleted−839
@@ -1,839 +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: "decorationsVisible"
180− value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized
181− && spread.state == ""
182− }
183−
184− Binding {
185− target: PanelState
186− property: "title"
187− value: {
188− if (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: "focusedPersistentSurfaceId"
202− value: {
203− if (priv.focusedAppDelegate !== null && spread.state == "") {
204− if (priv.focusedAppDelegate.surface) {
205− return priv.focusedAppDelegate.surface.persistentId;
206− }
207− }
208− return "";
209− }
210− when: priv.focusedAppDelegate
211− }
212−
213− Binding {
214− target: PanelState
215− property: "dropShadow"
216− value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppDelegate !== null
217− }
218−
219− Binding {
220− target: PanelState
221− property: "closeButtonShown"
222− value: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized && !priv.focusedAppDelegate.isDash
223− }
224−
225− Component.onDestruction: {
226− PanelState.title = "";
227− PanelState.decorationsVisible = false;
228− PanelState.dropShadow = false;
229− }
230−
231− Instantiator {
232− model: root.applicationManager
233− delegate: Binding {
234− target: model.application
235− property: "requestedState"
236−
237− // TODO: figure out some lifecycle policy, like suspending minimized apps
238− // if running on a tablet or something.
239− // TODO: If the device has a dozen suspended apps because it was running
240− // in staged mode, when it switches to Windowed mode it will suddenly
241− // resume all those apps at once. We might want to avoid that.
242− value: ApplicationInfoInterface.RequestedRunning // Always running for now
243− }
244− }
245−
246− Binding {
247− target: MirFocusController
248− property: "focusedSurface"
249− value: priv.focusedAppDelegate ? priv.focusedAppDelegate.focusedSurface : null
250− when: !appRepeater.startingUp && root.parent
251− }
252−
253− FocusScope {
254− id: appContainer
255− objectName: "appContainer"
256− anchors.fill: parent
257− focus: spread.state !== "altTab"
258−
259− Wallpaper {
260− id: wallpaper
261− anchors.fill: parent
262− source: root.background
263− }
264−
265− TopLevelSurfaceRepeater {
266− id: appRepeater
267− model: topLevelSurfaceList
268− objectName: "appRepeater"
269−
270− delegate: FocusScope {
271− id: appDelegate
272− objectName: "appDelegate_" + model.id
273− // z might be overriden in some cases by effects, but we need z ordering
274− // to calculate occlusion detection
275− property int normalZ: topLevelSurfaceList.count - index
276− onNormalZChanged: {
277− if (visuallyMaximized) {
278− priv.updateForegroundMaximizedApp();
279− }
280− }
281− z: normalZ
282− x: requestedX // may be overridden in some states. Do not directly write to this.
283− y: requestedY // may be overridden in some states. Do not directly write to this.
284− property real requestedX: priv.focusedAppDelegate ? priv.focusedAppDelegate.x + units.gu(3) : (normalZ - 1) * units.gu(3)
285− property real requestedY: priv.focusedAppDelegate ? priv.focusedAppDelegate.y + units.gu(3) : normalZ * units.gu(3)
286−
287− Binding {
288− target: appDelegate
289− property: "y"
290− value: appDelegate.requestedY -
291− Math.min(appDelegate.requestedY - PanelState.panelHeight,
292− Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
293− when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
294− && SurfaceManager.inputMethodSurface
295− && SurfaceManager.inputMethodSurface.state != Mir.HiddenState
296− && SurfaceManager.inputMethodSurface.state != Mir.MinimizedState
297−
298− }
299−
300− width: decoratedWindow.width
301− height: decoratedWindow.height
302−
303− Connections {
304− target: root
305− onShellOrientationAngleChanged: {
306− // at this point decoratedWindow.surfaceOrientationAngle is the old shellOrientationAngle
307− if (application && application.rotatesWindowContents) {
308− if (state == "normal" || state == "restored") {
309− var angleDiff = decoratedWindow.surfaceOrientationAngle - shellOrientationAngle;
310− angleDiff = (360 + angleDiff) % 360;
311− if (angleDiff === 90 || angleDiff === 270) {
312− var aux = decoratedWindow.requestedHeight;
313− decoratedWindow.requestedHeight = decoratedWindow.requestedWidth + decoratedWindow.visibleDecorationHeight;
314− decoratedWindow.requestedWidth = aux - decoratedWindow.visibleDecorationHeight;
315− }
316− }
317− decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
318− } else {
319− decoratedWindow.surfaceOrientationAngle = 0;
320− }
321− }
322− }
323−
324− readonly property alias application: decoratedWindow.application
325− readonly property alias minimumWidth: decoratedWindow.minimumWidth
326− readonly property alias minimumHeight: decoratedWindow.minimumHeight
327− readonly property alias maximumWidth: decoratedWindow.maximumWidth
328− readonly property alias maximumHeight: decoratedWindow.maximumHeight
329− readonly property alias widthIncrement: decoratedWindow.widthIncrement
330− readonly property alias heightIncrement: decoratedWindow.heightIncrement
331− property int requestedWidth: -1
332− property int requestedHeight: -1
333−
334− readonly property bool maximized: windowState === WindowStateStorage.WindowStateMaximized
335− readonly property bool maximizedLeft: windowState === WindowStateStorage.WindowStateMaximizedLeft
336− readonly property bool maximizedRight: windowState === WindowStateStorage.WindowStateMaximizedRight
337− readonly property bool maximizedHorizontally: windowState === WindowStateStorage.WindowStateMaximizedHorizontally
338− readonly property bool maximizedVertically: windowState === WindowStateStorage.WindowStateMaximizedVertically
339− readonly property bool maximizedTopLeft: windowState === WindowStateStorage.WindowStateMaximizedTopLeft
340− readonly property bool maximizedTopRight: windowState === WindowStateStorage.WindowStateMaximizedTopRight
341− readonly property bool maximizedBottomLeft: windowState === WindowStateStorage.WindowStateMaximizedBottomLeft
342− readonly property bool maximizedBottomRight: windowState === WindowStateStorage.WindowStateMaximizedBottomRight
343− readonly property bool anyMaximized: maximized || maximizedLeft || maximizedRight || maximizedHorizontally || maximizedVertically ||
344− maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
345−
346− readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
347− readonly property alias fullscreen: decoratedWindow.fullscreen
348−
349− readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
350− readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
351− (maximumHeight == 0 || maximumHeight >= appContainer.height)
352− readonly property bool canBeCornerMaximized: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
353− (maximumHeight == 0 || maximumHeight >= appContainer.height/2)
354− readonly property bool canBeMaximizedHorizontally: maximumWidth == 0 || maximumWidth >= appContainer.width
355− readonly property bool canBeMaximizedVertically: maximumHeight == 0 || maximumHeight >= appContainer.height
356−
357− property int windowState: WindowStateStorage.WindowStateNormal
358− property bool animationsEnabled: true
359− property alias title: decoratedWindow.title
360− readonly property string appName: model.application ? model.application.name : ""
361− property bool visuallyMaximized: false
362− property bool visuallyMinimized: false
363−
364− readonly property var surface: model.surface
365− readonly property alias resizeArea: resizeArea
366− readonly property alias focusedSurface: decoratedWindow.focusedSurface
367− readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
368−
369− readonly property bool isDash: model.application.appId == "unity8-dash"
370− readonly property alias clientAreaItem: decoratedWindow.clientAreaItem
371−
372− function claimFocus() {
373− if (spread.state == "altTab") {
374− spread.cancel();
375− }
376− appDelegate.restore(true /* animated */, appDelegate.windowState);
377− }
378− Connections {
379− target: model.surface
380− onFocusRequested: claimFocus();
381− }
382− Connections {
383− target: model.application
384− onFocusRequested: {
385− if (!model.surface) {
386− // when an app has no surfaces, we assume there's only one entry representing it:
387− // this delegate.
388− claimFocus();
389− } else {
390− // if the application has surfaces, focus request should be at surface-level.
391− }
392− }
393− }
394−
395− onFocusChanged: {
396− if (appRepeater.startingUp)
397− return;
398−
399− if (focus) {
400− // If we're orphan (!parent) it means this stage is no longer the current one
401− // and will be deleted shortly. So we should no longer have a say over the model
402− if (root.parent) {
403− topLevelSurfaceList.raiseId(model.id);
404− }
405−
406− priv.focusedAppDelegate = appDelegate;
407− } else if (!focus && priv.focusedAppDelegate === appDelegate) {
408− priv.focusedAppDelegate = null;
409− // FIXME: No idea why the Binding{} doens't update when focusedAppDelegate turns null
410− MirFocusController.focusedSurface = null;
411− }
412− }
413− Component.onCompleted: {
414− if (application && application.rotatesWindowContents) {
415− decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
416− } else {
417− decoratedWindow.surfaceOrientationAngle = 0;
418− }
419−
420− // NB: We're differentiating if this delegate was created in response to a new entry in the model
421− // or if the Repeater is just populating itself with delegates to match the model it received.
422− if (!appRepeater.startingUp) {
423− // a top level window is always the focused one when it first appears, unfocusing
424− // any preexisting one
425− focus = true;
426− }
427− }
428− Component.onDestruction: {
429− if (!root.parent) {
430− // This stage is about to be destroyed. Don't mess up with the model at this point
431− return;
432− }
433−
434− if (visuallyMaximized) {
435− priv.updateForegroundMaximizedApp();
436− }
437−
438− if (focus) {
439− // focus some other window
440− for (var i = 0; i < appRepeater.count; i++) {
441− var appDelegate = appRepeater.itemAt(i);
442− if (appDelegate && !appDelegate.minimized && i != index) {
443− appDelegate.focus = true;
444− return;
445− }
446− }
447− }
448− }
449−
450− onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
451−
452− visible: (
453− !visuallyMinimized
454− && !greeter.fullyShown
455− && (priv.foregroundMaximizedAppDelegate === null || priv.foregroundMaximizedAppDelegate.normalZ <= z)
456− )
457− || decoratedWindow.fullscreen
458− || (spread.state == "altTab" && index === spread.highlightedIndex)
459−
460− function close() {
461− model.surface.close();
462− }
463−
464− function maximize(animated) {
465− animationsEnabled = (animated === undefined) || animated;
466− windowState = WindowStateStorage.WindowStateMaximized;
467− }
468− function maximizeLeft(animated) {
469− animationsEnabled = (animated === undefined) || animated;
470− windowState = WindowStateStorage.WindowStateMaximizedLeft;
471− }
472− function maximizeRight(animated) {
473− animationsEnabled = (animated === undefined) || animated;
474− windowState = WindowStateStorage.WindowStateMaximizedRight;
475− }
476− function maximizeHorizontally(animated) {
477− animationsEnabled = (animated === undefined) || animated;
478− windowState = WindowStateStorage.WindowStateMaximizedHorizontally;
479− }
480− function maximizeVertically(animated) {
481− animationsEnabled = (animated === undefined) || animated;
482− windowState = WindowStateStorage.WindowStateMaximizedVertically;
483− }
484− function maximizeTopLeft(animated) {
485− animationsEnabled = (animated === undefined) || animated;
486− windowState = WindowStateStorage.WindowStateMaximizedTopLeft;
487− }
488− function maximizeTopRight(animated) {
489− animationsEnabled = (animated === undefined) || animated;
490− windowState = WindowStateStorage.WindowStateMaximizedTopRight;
491− }
492− function maximizeBottomLeft(animated) {
493− animationsEnabled = (animated === undefined) || animated;
494− windowState = WindowStateStorage.WindowStateMaximizedBottomLeft;
495− }
496− function maximizeBottomRight(animated) {
497− animationsEnabled = (animated === undefined) || animated;
498− windowState = WindowStateStorage.WindowStateMaximizedBottomRight;
499− }
500− function minimize(animated) {
501− animationsEnabled = (animated === undefined) || animated;
502− windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
503− }
504− function restoreFromMaximized(animated) {
505− animationsEnabled = (animated === undefined) || animated;
506− windowState = WindowStateStorage.WindowStateRestored;
507− }
508− function restore(animated,state) {
509− animationsEnabled = (animated === undefined) || animated;
510− windowState = state || WindowStateStorage.WindowStateRestored;
511− windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
512− focus = true;
513− }
514−
515− function playFocusAnimation() {
516− focusAnimation.start()
517− }
518−
519− UbuntuNumberAnimation {
520− id: focusAnimation
521− target: appDelegate
522− property: "scale"
523− from: 0.98
524− to: 1
525− duration: UbuntuAnimation.SnapDuration
526− }
527−
528− // unlike requestedX/Y, this is the last known grab position before being pushed against edges/corners
529− // when restoring, the window should return to these, not to the place where it was dropped near the edge
530− property real restoredX
531− property real restoredY
532−
533− states: [
534− State {
535− name: "fullscreen"; when: appDelegate.fullscreen && !appDelegate.minimized
536− PropertyChanges {
537− target: appDelegate;
538− x: rotation == 0 ? 0 : (parent.width - width) / 2 + (shellOrientationAngle == 90 ? -PanelState.panelHeight : PanelState.panelHeight)
539− y: rotation == 0 ? -PanelState.panelHeight : (parent.height - height) / 2
540− requestedWidth: appContainer.width;
541− requestedHeight: appContainer.height;
542− }
543− },
544− State {
545− name: "normal";
546− when: appDelegate.windowState == WindowStateStorage.WindowStateNormal
547− PropertyChanges {
548− target: appDelegate;
549− visuallyMinimized: false;
550− visuallyMaximized: false
551− }
552− },
553− State {
554− name: "restored";
555− when: appDelegate.windowState == WindowStateStorage.WindowStateRestored
556− extend: "normal"
557− PropertyChanges {
558− target: appDelegate;
559− requestedX: restoredX;
560− requestedY: restoredY;
561− }
562− },
563− State {
564− name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
565− PropertyChanges {
566− target: appDelegate;
567− requestedX: root.leftMargin;
568− requestedY: 0;
569− requestedWidth: appContainer.width - root.leftMargin;
570− requestedHeight: appContainer.height;
571− visuallyMinimized: false;
572− visuallyMaximized: true
573− }
574− },
575− State {
576− name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
577− PropertyChanges {
578− target: appDelegate
579− requestedX: root.leftMargin
580− requestedY: PanelState.panelHeight
581− requestedWidth: (appContainer.width - root.leftMargin)/2
582− requestedHeight: appContainer.height - PanelState.panelHeight
583− }
584− },
585− State {
586− name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
587− extend: "maximizedLeft"
588− PropertyChanges {
589− target: appDelegate;
590− requestedX: (appContainer.width + root.leftMargin)/2
591− }
592− },
593− State {
594− name: "maximizedTopLeft"; when: appDelegate.maximizedTopLeft && !appDelegate.minimized
595− PropertyChanges {
596− target: appDelegate
597− requestedX: root.leftMargin
598− requestedY: PanelState.panelHeight
599− requestedWidth: (appContainer.width - root.leftMargin)/2
600− requestedHeight: (appContainer.height - PanelState.panelHeight)/2
601− }
602− },
603− State {
604− name: "maximizedTopRight"; when: appDelegate.maximizedTopRight && !appDelegate.minimized
605− extend: "maximizedTopLeft"
606− PropertyChanges {
607− target: appDelegate
608− requestedX: (appContainer.width + root.leftMargin)/2
609− }
610− },
611− State {
612− name: "maximizedBottomLeft"; when: appDelegate.maximizedBottomLeft && !appDelegate.minimized
613− PropertyChanges {
614− target: appDelegate
615− requestedX: root.leftMargin
616− requestedY: (appContainer.height + PanelState.panelHeight)/2
617− requestedWidth: (appContainer.width - root.leftMargin)/2
618− requestedHeight: appContainer.height/2
619− }
620− },
621− State {
622− name: "maximizedBottomRight"; when: appDelegate.maximizedBottomRight && !appDelegate.minimized
623− extend: "maximizedBottomLeft"
624− PropertyChanges {
625− target: appDelegate
626− requestedX: (appContainer.width + root.leftMargin)/2
627− }
628− },
629− State {
630− name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
631− PropertyChanges { target: appDelegate; requestedX: root.leftMargin; requestedY: requestedY; requestedWidth: appContainer.width - root.leftMargin }
632− },
633− State {
634− name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
635− PropertyChanges { target: appDelegate; requestedX: requestedX; requestedY: PanelState.panelHeight; requestedHeight: appContainer.height - PanelState.panelHeight }
636− },
637− State {
638− name: "minimized"; when: appDelegate.minimized
639− PropertyChanges {
640− target: appDelegate;
641− requestedX: -appDelegate.width / 2;
642− scale: units.gu(5) / appDelegate.width;
643− opacity: 0;
644− visuallyMinimized: true;
645− visuallyMaximized: false
646− }
647− }
648− ]
649− transitions: [
650− Transition {
651− to: "normal,restored"
652− enabled: appDelegate.animationsEnabled
653− PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
654− UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
655− },
656− Transition {
657− to: "minimized"
658− enabled: appDelegate.animationsEnabled
659− PropertyAction { target: appDelegate; property: "visuallyMaximized" }
660− SequentialAnimation {
661− UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
662− PropertyAction { target: appDelegate; property: "visuallyMinimized" }
663− ScriptAction {
664− script: {
665− if (appDelegate.minimized) {
666− appDelegate.focus = false;
667− priv.focusNext();
668− }
669− }
670− }
671− }
672− },
673− Transition {
674− to: "*" //maximized and fullscreen
675− enabled: appDelegate.animationsEnabled
676− PropertyAction { target: appDelegate; property: "visuallyMinimized" }
677− SequentialAnimation {
678− UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
679− PropertyAction { target: appDelegate; property: "visuallyMaximized" }
680− ScriptAction { script: { fakeRectangle.stop(); } }
681− }
682− }
683− ]
684−
685− Binding {
686− id: previewBinding
687− target: appDelegate
688− property: "z"
689− value: topLevelSurfaceList.count + 1
690− when: index == spread.highlightedIndex && spread.ready
691− }
692−
693− Binding {
694− target: PanelState
695− property: "decorationsAlwaysVisible"
696− value: appDelegate && appDelegate.maximized && touchControls.overlayShown
697− }
698−
699− WindowResizeArea {
700− id: resizeArea
701− objectName: "windowResizeArea"
702−
703− // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
704− anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
705−
706− target: appDelegate
707− minWidth: units.gu(10)
708− minHeight: units.gu(10)
709− borderThickness: units.gu(2)
710− windowId: model.application.appId // FIXME: Change this to point to windowId once we have such a thing
711− screenWidth: appContainer.width
712− screenHeight: appContainer.height
713− leftMargin: root.leftMargin
714−
715− onPressed: { appDelegate.focus = true; }
716−
717− Component.onCompleted: {
718− loadWindowState();
719− }
720−
721− property bool saveStateOnDestruction: true
722− Connections {
723− target: root
724− onStageAboutToBeUnloaded: {
725− resizeArea.saveWindowState();
726− resizeArea.saveStateOnDestruction = false;
727− fullscreenPolicy.active = false;
728− }
729− }
730− Component.onDestruction: {
731− if (saveStateOnDestruction) {
732− saveWindowState();
733− }
734− }
735− }
736−
737− DecoratedWindow {
738− id: decoratedWindow
739− objectName: "decoratedWindow"
740− anchors.left: appDelegate.left
741− anchors.top: appDelegate.top
742− application: model.application
743− surface: model.surface
744− active: appDelegate.focus
745− focus: true
746− maximizeButtonShown: appDelegate.canBeMaximized
747− overlayShown: touchControls.overlayShown
748− stageWidth: appContainer.width
749− stageHeight: appContainer.height
750−
751− requestedWidth: appDelegate.requestedWidth
752− requestedHeight: appDelegate.requestedHeight
753−
754− onCloseClicked: { appDelegate.close(); }
755− onMaximizeClicked: appDelegate.anyMaximized ? appDelegate.restoreFromMaximized() : appDelegate.maximize();
756− onMaximizeHorizontallyClicked: appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
757− onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
758− onMinimizeClicked: appDelegate.minimize()
759− onDecorationPressed: { appDelegate.focus = true; }
760− onDecorationReleased: fakeRectangle.commit();
761− }
762−
763− WindowControlsOverlay {
764− id: touchControls
765− target: appDelegate
766− stageWidth: appContainer.width
767− stageHeight: appContainer.height
768−
769− onFakeMaximizeAnimationRequested: if (!appDelegate.maximized) fakeRectangle.maximize(amount, true)
770− onFakeMaximizeLeftAnimationRequested: if (!appDelegate.maximizedLeft) fakeRectangle.maximizeLeft(amount, true)
771− onFakeMaximizeRightAnimationRequested: if (!appDelegate.maximizedRight) fakeRectangle.maximizeRight(amount, true)
772− onFakeMaximizeTopLeftAnimationRequested: if (!appDelegate.maximizedTopLeft) fakeRectangle.maximizeTopLeft(amount, true);
773− onFakeMaximizeTopRightAnimationRequested: if (!appDelegate.maximizedTopRight) fakeRectangle.maximizeTopRight(amount, true);
774− onFakeMaximizeBottomLeftAnimationRequested: if (!appDelegate.maximizedBottomLeft) fakeRectangle.maximizeBottomLeft(amount, true);
775− onFakeMaximizeBottomRightAnimationRequested: if (!appDelegate.maximizedBottomRight) fakeRectangle.maximizeBottomRight(amount, true);
776− onStopFakeAnimation: fakeRectangle.stop();
777− onDragReleased: fakeRectangle.commit();
778− }
779−
780− WindowedFullscreenPolicy {
781− id: fullscreenPolicy
782− active: true
783− surface: model.surface
784− }
785− }
786− }
787− }
788−
789− FakeMaximizeDelegate {
790− id: fakeRectangle
791− target: priv.focusedAppDelegate
792− leftMargin: root.leftMargin
793− appContainerWidth: appContainer.width
794− appContainerHeight: appContainer.height
795− }
796−
797− EdgeBarrier {
798− id: edgeBarrier
799−
800− // NB: it does its own positioning according to the specified edge
801− edge: Qt.RightEdge
802−
803− onPassed: { spread.show(); }
804− material: Component {
805− Item {
806− Rectangle {
807− width: parent.height
808− height: parent.width
809− rotation: 90
810− anchors.centerIn: parent
811− gradient: Gradient {
812− GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
813− GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
814− }
815− }
816− }
817− }
818− }
819−
820− SwipeArea {
821− direction: Direction.Leftwards
822− anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
823− width: units.gu(1)
824− onDraggingChanged: { if (dragging) { spread.show(); } }
825− }
826−
827− DesktopSpread {
828− id: spread
829− objectName: "spread"
830− anchors.fill: appContainer
831− workspace: appContainer
832− focus: state == "altTab"
833− altTabPressed: root.altTabPressed
834−
835− onPlayFocusAnimation: {
836− appRepeater.itemAt(index).playFocusAnimation();
837− }
838− }
839−}
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/SurfaceManager.cpp+7−6
@@ -36,11 +36,11 @@ SurfaceManager::SurfaceManager(QObject *parent) :
3636 the_surface_manager = this;
3737
3838 m_virtualKeyboard = new VirtualKeyboard;
39− connect(m_virtualKeyboard, &QObject::destroyed, this, [this](QObject *obj) {
40− MirSurface* surface = qobject_cast<MirSurface*>(obj);
39+ const QString persistentId = m_virtualKeyboard->persistentId();
40+ connect(m_virtualKeyboard, &QObject::destroyed, this, [this, persistentId](QObject*) {
4141 m_virtualKeyboard = nullptr;
4242 Q_EMIT inputMethodSurfaceChanged();
43− Q_EMIT surfaceDestroyed(surface);
43+ Q_EMIT surfaceDestroyed(persistentId);
4444 });
4545}
4646
@@ -56,9 +56,10 @@ MirSurface *SurfaceManager::createSurface(const QString& name,
5656 const QUrl& screenshot)
5757{
5858 MirSurface* surface = new MirSurface(name, type, state, screenshot);
59− connect(surface, &QObject::destroyed, this, [this](QObject *obj) {
60− MirSurface* surface = qobject_cast<MirSurface*>(obj);
61− Q_EMIT surfaceDestroyed(surface);
59+ const QString persistentId = surface->persistentId();
60+
61+ connect(surface, &QObject::destroyed, this, [this, persistentId](QObject*) {
62+ Q_EMIT surfaceDestroyed(persistentId);
6263 });
6364
6465 surface->setMinimumWidth(m_newSurfaceMinimumWidth);
tests/mocks/Unity/Application/SurfaceManager.h+1−1
@@ -70,7 +70,7 @@ Q_SIGNALS:
7070 void inputMethodSurfaceChanged();
7171 void countChanged();
7272 void surfaceCreated(MirSurface *surface);
73− void surfaceDestroyed(MirSurface*surface);
73+ void surfaceDestroyed(const QString& persistentSurfaceId);
7474
7575 void newSurfaceMinimumWidthChanged(int value);
7676 void newSurfaceMaximumWidthChanged(int value);
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/mocks/Unity/ApplicationMenu/mockapplicationmenuregistry.cpp+5
@@ -40,3 +40,8 @@ void MockApplicationMenuRegistry::RegisterSurfaceMenu(const QString &surface,
4040 QDBusObjectPath(actionObjectPath),
4141 service);
4242}
43+
44+void MockApplicationMenuRegistry::UnregisterSurfaceMenu(const QString &surfaceId, const QDBusObjectPath &menuObjectPath)
45+{
46+ ApplicationMenuRegistry::UnregisterSurfaceMenu(surfaceId, menuObjectPath);
47+}
tests/mocks/Unity/ApplicationMenu/mockapplicationmenuregistry.h+3
@@ -31,6 +31,9 @@ public:
3131 const QString &actionObjectPath,
3232 const QString &service);
3333
34+ Q_INVOKABLE void UnregisterSurfaceMenu(const QString &surfaceId,
35+ const QDBusObjectPath &menuObjectPath);
36+
3437private:
3538 MockApplicationMenuRegistry(QObject *parent = 0);
3639};
tests/qmltests/ApplicationMenuDataLoader.qmlnew file+54
@@ -0,0 +1,54 @@
1+import QtQuick 2.4
2+import Ubuntu.Components 1.3
3+import Unity.Application 0.1
4+import Unity.ApplicationMenu 0.1
5+import Unity.Indicators 0.1 as Indicators
6+
7+Object {
8+ Connections {
9+ target: SurfaceManager
10+ onSurfaceCreated: {
11+ var fakeMenuPath = "/" + surface.persistentId.replace(/\W+/g, "");;
12+
13+ ApplicationMenuRegistry.RegisterSurfaceMenu(surface.persistentId, fakeMenuPath, fakeMenuPath, ":1");
14+ Indicators.UnityMenuModelCache.setCachedModelData(fakeMenuPath, generateTestData(5, 3, 3, "menu"));
15+ }
16+ onSurfaceDestroyed: {
17+ ApplicationMenuRegistry.UnregisterSurfaceMenu(persistentSurfaceId, "/app");
18+ }
19+ }
20+
21+ function generateTestData(length, depth, separatorInterval, prefix) {
22+ var data = [];
23+
24+ if (prefix === undefined) prefix = "menu"
25+
26+ for (var i = 0; i < length; i++) {
27+
28+ var menuCode = String.fromCharCode(i+65);
29+
30+ var isSeparator = separatorInterval > 0 && ((i+1) % separatorInterval == 0);
31+ var row = {
32+ "rowData": { // 1
33+ "label": prefix + "&" + menuCode,
34+ "sensitive": true,
35+ "isSeparator": isSeparator,
36+ "icon": "",
37+ "ext": {},
38+ "action": prefix + menuCode,
39+ "actionState": {},
40+ "isCheck": false,
41+ "isRadio": false,
42+ "isToggled": false,
43+ "shortcut": ""
44+ }
45+ }
46+ if (!isSeparator && depth > 1) {
47+ var submenu = generateTestData(length, depth-1, separatorInterval, prefix + menuCode + ".");
48+ row["submenu"] = submenu;
49+ }
50+ data[i] = row;
51+ }
52+ return data;
53+ }
54+}
tests/qmltests/CMakeLists.txt+9−10
@@ -88,16 +88,15 @@ add_unity8_qmltest(Panel/Indicators IndicatorItem)
8888add_unity8_qmltest(Panel/Indicators IndicatorsLight)
8989add_unity8_qmltest(Panel/Indicators MenuItemFactory)
9090add_unity8_qmltest(Panel/Indicators MessageMenuItemFactory)
91−add_unity8_qmltest(Stages ApplicationWindow)
92−add_unity8_qmltest(Stages DecoratedWindow)
93−add_unity8_qmltest(Stages DesktopStage)
94−add_unity8_qmltest(Stages PhoneStage)
95−add_unity8_qmltest(Stages SpreadDelegate)
96−add_unity8_qmltest(Stages SurfaceContainer)
97−add_unity8_qmltest(Stages TabletStage)
98−add_unity8_qmltest(Stages WindowResizeArea)
99−add_unity8_qmltest(Stages WindowDecoration)
100−add_unity8_qmltest(Stages Splash)
91+add_unity8_qmltest(Stage ApplicationWindow)
92+add_unity8_qmltest(Stage DesktopStage)
93+add_unity8_qmltest(Stage PhoneStage)
94+add_unity8_qmltest(Stage SurfaceContainer)
95+add_unity8_qmltest(Stage TabletStage)
96+add_unity8_qmltest(Stage WindowResizeArea)
97+add_unity8_qmltest(Stage Splash)
98+add_unity8_qmltest(Stage DecoratedWindow)
99+add_unity8_qmltest(Stage WindowDecoration)
101100add_unity8_qmltest(Tutorial Tutorial)
102101add_unity8_qmltest(Wizard Wizard ENVIRONMENT "OXIDE_NO_SANDBOX=1")
103102add_unity8_qmltest(utils/Unity/Test UnityTest)
tests/qmltests/Panel/tst_Panel.qml−3
@@ -46,9 +46,6 @@ PanelTest {
4646
4747 Component.onCompleted: {
4848 theme.name = "Ubuntu.Components.Themes.SuruDark"
49−
50− ApplicationMenuRegistry.RegisterSurfaceMenu("dialerAppSurfaceId", "/dialerapp", "/dialerapp", ":2");
51− Indicators.UnityMenuModelCache.setCachedModelData("/dialerapp", appMenuData.dialerData);
5249 }
5350
5451 Rectangle {
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+260
@@ -0,0 +1,260 @@
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/Components/PanelState"
23+import "../../../qml/Stage"
24+import Ubuntu.Components 1.3
25+import Ubuntu.Components.ListItems 1.3 as ListItem
26+import Unity.Application 0.1
27+import Unity.ApplicationMenu 0.1
28+import Unity.Indicators 0.1 as Indicators
29+
30+Rectangle {
31+ color: "red"
32+ id: root
33+ width: fakeShell.shortestDimension + controls.width
34+ height: fakeShell.longestDimension
35+
36+ Component.onCompleted: {
37+ QuickUtils.keyboardAttached = true;
38+ theme.name = "Ubuntu.Components.Themes.SuruDark"
39+ }
40+
41+ property QtObject fakeApplication: null
42+
43+ Binding {
44+ target: UT.MouseTouchAdaptor
45+ property: "enabled"
46+ value: false
47+ }
48+
49+ ApplicationMenuDataLoader { }
50+
51+ Item {
52+ id: fakeShell
53+
54+ readonly property real shortestDimension: units.gu(100)
55+ readonly property real longestDimension: units.gu(70)
56+
57+ width: landscape ? longestDimension : shortestDimension
58+ height: landscape ? shortestDimension : longestDimension
59+
60+ x: landscape ? (height - width) / 2 : 0
61+ y: landscape ? (width - height) / 2 : 0
62+
63+ property bool landscape: orientationAngle == 90 || orientationAngle == 270
64+ property int orientationAngle: shellOrientationAngleSelector.value
65+
66+ rotation: orientationAngle
67+
68+ WindowResizeArea {
69+ target: loader
70+ borderThickness: units.gu(2)
71+ }
72+
73+ Loader {
74+ id: loader
75+ property int windowedX: units.gu(1)
76+ property int windowedY: units.gu(1)
77+ property int windowedWidth: units.gu(50)
78+ property int windowedHeight: units.gu(40)
79+ x: windowedX
80+ y: windowedY
81+ width: item ? item.implicitWidth : 0
82+ height: item ? item.implicitHeight : 0
83+
84+ active: false
85+ property bool itemDestroyed: false
86+
87+ sourceComponent: DecoratedWindow {
88+ id: decoratedWindow
89+
90+ anchors.fill: parent
91+ application: fakeApplication
92+ requestedWidth: loader.windowedWidth
93+ requestedHeight: loader.windowedHeight
94+ active: true
95+ surface: fakeApplication && fakeApplication.surfaceList.count > 0 ? fakeApplication.surfaceList.get(0) : null
96+
97+ Binding {
98+ target: PanelState
99+ property: "focusedPersistentSurfaceId"
100+ value: decoratedWindow.surface ? decoratedWindow.surface.persistentId : "x"
101+ }
102+
103+ Component.onCompleted: {
104+ loader.itemDestroyed = false;
105+ }
106+ }
107+
108+ Rectangle { anchors.fill: parent; color: "green"; opacity: 1 }
109+ }
110+ }
111+
112+
113+ Rectangle {
114+ id: controls
115+ color: theme.palette.normal.background
116+ width: units.gu(30)
117+ anchors {
118+ top: parent.top
119+ bottom: parent.bottom
120+ right: parent.right
121+ }
122+
123+ Column {
124+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
125+ spacing: units.gu(1)
126+ Button {
127+ id: loadWidthDialerApp
128+ text: "Load with dialer-app"
129+ onClicked: { testCase.restartWithApp("dialer-app"); }
130+ }
131+ Button {
132+ id: loadWithWeatherApp
133+ text: "Load with ubuntu-weather-app"
134+ onClicked: { testCase.restartWithApp("ubuntu-weather-app"); }
135+ }
136+ Button {
137+ id: loadWithGalleryApp
138+ text: "Load with gallery-app"
139+ onClicked: { testCase.restartWithApp("gallery-app"); }
140+ }
141+ Button {
142+ text: "toggle has decoration"
143+ onClicked: loader.item.hasDecoration = !loader.item.hasDecoration
144+ }
145+ Button {
146+ text: "show/hide decoration"
147+ onClicked: loader.item.showDecoration = !loader.item.showDecoration
148+ }
149+ Label {
150+ text: "scale to preview progress"
151+ }
152+
153+ Slider {
154+ value: 0
155+ minimumValue: 0
156+ maximumValue: 1
157+ live: true
158+ onValueChanged: loader.item.scaleToPreviewProgress = value
159+ }
160+ ListItem.ItemSelector {
161+ id: shellOrientationAngleSelector
162+ anchors { left: parent.left; right: parent.right }
163+ text: "shellOrientationAngle"
164+ model: ["0", "90", "180", "270"]
165+ property int value: selectedIndex * 90
166+ }
167+ Button {
168+ text: "matchShellOrientation()"
169+ onClicked: { loader.item.matchShellOrientation(); }
170+ }
171+ Button {
172+ text: "animateToShellOrientation()"
173+ onClicked: { loader.item.animateToShellOrientation(); }
174+ }
175+ }
176+ }
177+
178+ UT.UnityTestCase {
179+ id: testCase
180+ name: "DecoratedWindow"
181+ when: windowShown
182+
183+ property Item decoratedWindow: loader.item
184+
185+ function init() {
186+ }
187+
188+ function cleanup() {
189+ unloadWindow();
190+ root.fakeApplication = null;
191+ killApps();
192+ }
193+
194+ function restartWithApp(appId) {
195+ if (loader.active) {
196+ unloadWindow();
197+ }
198+ if (root.fakeApplication) {
199+ ApplicationManager.stopApplication(root.fakeApplication.appId);
200+ }
201+
202+ root.fakeApplication = ApplicationManager.startApplication(appId);
203+ loader.active = true;
204+ tryCompare(loader, "status", Loader.Ready);
205+ }
206+
207+ function unloadWindow() {
208+ loader.active = false;
209+ tryCompare(loader, "status", Loader.Null);
210+ tryCompare(loader, "item", null);
211+ // Loader.status might be Loader.Null and Loader.item might be null but the Loader
212+ // item might still be alive. So if we set Loader.active back to true
213+ // again right now we will get the very same Shell instance back. So no reload
214+ // actually took place. Likely because Loader waits until the next event loop
215+ // iteration to do its work. So to ensure the reload, we will wait until the
216+ // Shell instance gets destroyed.
217+ tryCompare(loader, "itemDestroyed", true);
218+
219+ }
220+
221+ function test_keepsSceneTransformationWhenShellRotates_data() {
222+ return [
223+ {tag: "0", selectedIndex: 0},
224+ {tag: "90", selectedIndex: 1},
225+ {tag: "180", selectedIndex: 2},
226+ {tag: "270", selectedIndex: 3}
227+ ];
228+ }
229+ function test_keepsSceneTransformationWhenShellRotates(data) {
230+ loadWithGalleryApp.clicked();
231+
232+ // Wait until it reaches the state we are interested on.
233+ // It begins with "startingUp"
234+ tryCompare(decoratedWindow, "counterRotate", false);
235+ var oldWidth = decoratedWindow.width
236+ var oldHeight = decoratedWindow.height
237+ var oldX = decoratedWindow.x
238+ var oldY = decoratedWindow.y
239+
240+ shellOrientationAngleSelector.selectedIndex = data.selectedIndex;
241+
242+ // must keep same aspect ratio
243+ compare(decoratedWindow.width, oldWidth);
244+ compare(decoratedWindow.height, oldHeight);
245+
246+
247+ // and scene transformation must be the identity (ie, no rotation or translation)
248+ compare(decoratedWindow.x, oldX);
249+ compare(decoratedWindow.y, oldY);
250+ }
251+
252+ function test_showHighLight() {
253+ loadWithGalleryApp.clicked();
254+ var highlightRect = findChild(loader.item, "selectionHighlight")
255+ tryCompare(highlightRect, "visible", false)
256+ loader.item.showHighlight = true;
257+ tryCompare(highlightRect, "visible", true)
258+ }
259+ }
260+}
tests/qmltests/Stages/tst_DesktopStage.qml → tests/qmltests/Stage/tst_DesktopStage.qmlrenamed+30−83
@@ -26,14 +26,14 @@ import WindowManager 0.1
2626import Utils 0.1
2727
2828import ".." // For EdgeBarrierControls
29−import "../../../qml/Stages"
29+import "../../../qml/Stage"
3030import "../../../qml/Components"
3131import "../../../qml/Components/PanelState"
3232
3333Item {
3434 id: root
35− width: desktopStageLoader.width + controls.width
36− height: desktopStageLoader.height
35+ width: stageLoader.width + controls.width
36+ height: stageLoader.height
3737
3838 property var greeter: { fullyShown: true }
3939
@@ -43,23 +43,12 @@ Item {
4343 value: false
4444 }
4545
46− DesktopMenuData {
47− id: appMenuData
48− }
46+ ApplicationMenuDataLoader { }
4947
5048 Component.onCompleted: {
5149 QuickUtils.keyboardAttached = true;
5250 theme.name = "Ubuntu.Components.Themes.SuruDark";
5351 resetGeometry();
54−
55− ApplicationMenuRegistry.RegisterSurfaceMenu("DialerId", "/dialerapp", "/dialerapp", ":0");
56− Indicators.UnityMenuModelCache.setCachedModelData("/dialerapp", appMenuData.dialerData);
57−
58− ApplicationMenuRegistry.RegisterSurfaceMenu("CameraId", "/cameraapp", "/cameraapp", ":1");
59− Indicators.UnityMenuModelCache.setCachedModelData("/cameraapp", appMenuData.cameraData);
60−
61− ApplicationMenuRegistry.RegisterSurfaceMenu("GalleryId", "/galleryapp", "/galleryapp", ":2");
62− Indicators.UnityMenuModelCache.setCachedModelData("/galleryapp", appMenuData.galleryData);
6352 }
6453
6554 function resetGeometry() {
@@ -79,7 +68,7 @@ Item {
7968 }
8069
8170 Loader {
82− id: desktopStageLoader
71+ id: stageLoader
8372 x: ((root.width - controls.width) - width) / 2
8473 y: (root.height - height) / 2
8574 width: units.gu(160*0.9)
@@ -89,7 +78,7 @@ Item {
8978
9079 property bool itemDestroyed: false
9180 sourceComponent: Component {
92− DesktopStage {
81+ Stage {
9382 anchors.fill: parent
9483 background: "/usr/share/backgrounds/warty-final-ubuntu.png"
9584 focus: true
@@ -98,17 +87,19 @@ Item {
9887 edgeBarrierControls.target = testCase.findChild(this, "edgeBarrierController");
9988 }
10089 Component.onDestruction: {
101− desktopStageLoader.itemDestroyed = true;
90+ stageLoader.itemDestroyed = true;
10291 }
10392 orientations: Orientations {}
10493 applicationManager: ApplicationManager
10594 topLevelSurfaceList: topSurfaceList
95+ interactive: true
96+ mode: "windowed"
10697 }
10798 }
10899
109100 MouseArea {
110101 id: clickThroughTester
111− anchors.fill: desktopStageLoader.item
102+ anchors.fill: stageLoader.item
112103 acceptedButtons: Qt.AllButtons
113104 hoverEnabled: true
114105 }
@@ -146,7 +137,7 @@ Item {
146137 id: edgeBarrierControls
147138 text: "Drag here to pull out spread"
148139 backgroundColor: "blue"
149− onDragged: { desktopStageLoader.item.pushRightEdge(amount); }
140+ onDragged: { stageLoader.item.pushRightEdge(amount); }
150141 }
151142
152143 Divider {}
@@ -173,7 +164,7 @@ Item {
173164 name: "DesktopStage"
174165 when: windowShown
175166
176− property Item desktopStage: desktopStageLoader.status === Loader.Ready ? desktopStageLoader.item : null
167+ property Item stage: stageLoader.status === Loader.Ready ? stageLoader.item : null
177168
178169 function init() {
179170 // wait until unity8-dash is up and running.
@@ -189,29 +180,29 @@ Item {
189180 }
190181
191182 function cleanup() {
192− desktopStageLoader.itemDestroyed = false;
193− desktopStageLoader.active = false;
183+ stageLoader.itemDestroyed = false;
184+ stageLoader.active = false;
194185
195− tryCompare(desktopStageLoader, "status", Loader.Null);
196− tryCompare(desktopStageLoader, "item", null);
186+ tryCompare(stageLoader, "status", Loader.Null);
187+ tryCompare(stageLoader, "item", null);
197188 // Loader.status might be Loader.Null and Loader.item might be null but the Loader
198189 // actually took place. Likely because Loader waits until the next event loop
199190 // iteration to do its work. So to ensure the reload, we will wait until the
200191 // Shell instance gets destroyed.
201− tryCompare(desktopStageLoader, "itemDestroyed", true);
192+ tryCompare(stageLoader, "itemDestroyed", true);
202193
203194 killApps();
204195
205196 root.resetGeometry();
206197
207− desktopStageLoader.active = true;
208− tryCompare(desktopStageLoader, "status", Loader.Ready);
198+ stageLoader.active = true;
199+ tryCompare(stageLoader, "status", Loader.Ready);
209200
210201 mouseEaterSpy.clear();
211202 }
212203
213204 function waitUntilAppSurfaceShowsUp(surfaceId) {
214− var appDelegate = findChild(desktopStage, "appDelegate_" + surfaceId);
205+ var appDelegate = findChild(stage, "appDelegate_" + surfaceId);
215206 verify(appDelegate);
216207 var appWindow = findChild(appDelegate, "appWindow");
217208 verify(appWindow);
@@ -229,7 +220,7 @@ Item {
229220 if (app) {
230221 for (var i = 0; i < topSurfaceList.count; i++) {
231222 if (topSurfaceList.applicationAt(i).appId === appId) {
232− var appRepeater = findChild(desktopStage, "appRepeater");
223+ var appRepeater = findChild(stage, "appRepeater");
233224 verify(appRepeater);
234225 return appRepeater.itemAt(i);
235226 }
@@ -242,7 +233,7 @@ Item {
242233 waitUntilAppSurfaceShowsUp(surfaceId);
243234 compare(app.surfaceList.count, 1);
244235
245− return findChild(desktopStage, "appDelegate_" + surfaceId);
236+ return findChild(stage, "appDelegate_" + surfaceId);
246237 } catch(err) {
247238 throw new Error("startApplication("+appId+") called from line " + util.callerLine(1) + " failed!");
248239 }
@@ -340,7 +331,7 @@ Item {
340331 }
341332
342333 function findDecoratedWindow(surfaceId) {
343− var appDelegate = findChild(desktopStage, "appDelegate_" + surfaceId);
334+ var appDelegate = findChild(stage, "appDelegate_" + surfaceId);
344335 if (!appDelegate) {
345336 console.warn("Could not find appDelegate for surfaceId="+surfaceId);
346337 return null;
@@ -349,7 +340,7 @@ Item {
349340 }
350341
351342 function findWindowDecoration(surfaceId) {
352− var appDelegate = findChild(desktopStage, "appDelegate_" + surfaceId);
343+ var appDelegate = findChild(stage, "appDelegate_" + surfaceId);
353344 if (!appDelegate) {
354345 console.warn("Could not find appDelegate for surfaceId="+surfaceId);
355346 return null;
@@ -481,8 +472,8 @@ Item {
481472
482473 mouseClick(dialerDelegate);
483474
484− var desktopStagePriv = findInvisibleChild(desktopStage, "DesktopStagePrivate");
485− tryCompare(desktopStagePriv, "focusedAppDelegate", dialerDelegate);
475+ var stagePriv = findInvisibleChild(stage, "DesktopStagePrivate");
476+ tryCompare(stagePriv, "focusedAppDelegate", dialerDelegate);
486477
487478 keyClick(Qt.Key_F4, Qt.AltModifier); // Alt+F4 shortcut to close
488479
@@ -533,35 +524,6 @@ Item {
533524 tryCompare(MirFocusController, "focusedSurface", null); // verify still no surface is focused
534525 }
535526
536− function test_oskDisplacesWindow_data() {
537− return [
538− {tag: "no need to displace", windowHeight: units.gu(10), windowY: units.gu(5), targetDisplacement: units.gu(5), oskEnabled: true},
539− {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight, oskEnabled: true},
540− {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight, oskEnabled: true},
541− {tag: "osk not on this screen", windowHeight: units.gu(40), windowY: units.gu(10), targetDisplacement: units.gu(10), oskEnabled: false},
542− ]
543− }
544−
545− function test_oskDisplacesWindow(data) {
546− desktopStageLoader.item.oskEnabled = data.oskEnabled
547− var dashAppDelegate = startApplication("unity8-dash");
548− var oldOSKState = SurfaceManager.inputMethodSurface.state;
549− SurfaceManager.inputMethodSurface.state = Mir.RestoredState;
550− verify(dashAppDelegate);
551− dashAppDelegate.requestedHeight = data.windowHeight;
552− dashAppDelegate.requestedY = data.windowY;
553− SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
554− var initialY = dashAppDelegate.y;
555− verify(initialY > PanelState.panelHeight);
556−
557− SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, root.height / 2, root.width, root.height / 2));
558− tryCompare(dashAppDelegate, "y", data.targetDisplacement);
559−
560− SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
561− tryCompare(dashAppDelegate, "y", initialY);
562− SurfaceManager.inputMethodSurface.state = oldOSKState;
563− }
564−
565527 function test_minimizeApplicationHidesSurface() {
566528 compare(topSurfaceList.applicationAt(0).appId, "unity8-dash");
567529 var dashSurface = topSurfaceList.surfaceAt(0);
@@ -614,6 +576,8 @@ Item {
614576 var gmailDelegate = startApplication("gmail-webapp");
615577 verify(gmailDelegate);
616578
579+ wait(2000)
580+
617581 var gmailMaximizeButton = findChild(gmailDelegate, "maximizeWindowButton");
618582 verify(gmailMaximizeButton);
619583 mouseClick(gmailMaximizeButton);
@@ -626,6 +590,7 @@ Item {
626590 tryCompare(mapApp.surfaceList.get(0), "visible", false);
627591
628592 ApplicationManager.stopApplication("gmail-webapp");
593+ wait(2000)
629594
630595 tryCompare(mapApp.surfaceList.get(0), "visible", true);
631596 tryCompare(dialerApp.surfaceList.get(0), "visible", true);
@@ -725,7 +690,7 @@ Item {
725690 tryCompare(overlay, "visible", data.result);
726691
727692 if (data.result) { // if shown, try to hide it by clicking outside
728− mouseClick(desktopStage);
693+ mouseClick(stage);
729694 tryCompare(overlay, "visible", false);
730695 }
731696 }
@@ -784,24 +749,6 @@ Item {
784749 tryCompareFunction(function(){return posBefore == posAfter;}, data.button !== Qt.LeftButton ? true : false);
785750 }
786751
787− function test_preventMouseEventsThruDesktopSpread() {
788− var spread = findChild(desktopStage, "spread");
789− verify(spread);
790− spread.show();
791− tryCompareFunction( function(){ return spread.ready }, true );
792−
793− mouseEaterSpy.signalName = "wheel";
794− mouseWheel(spread, spread.width/2, spread.height/2, 10, 10);
795− tryCompare(mouseEaterSpy, "count", 0);
796−
797− mouseEaterSpy.clear();
798− mouseEaterSpy.signalName = "clicked";
799− mouseClick(spread, spread.width/2, spread.height/2, Qt.RightButton);
800− tryCompare(mouseEaterSpy, "count", 0);
801−
802− spread.cancel();
803− }
804−
805752 function test_eatWindowDecorationMouseEvents_data() {
806753 return [
807754 {tag: "left mouse click", signalName: "clicked", button: Qt.LeftButton },
tests/qmltests/Stages/tst_PhoneStage.qml → tests/qmltests/Stage/tst_PhoneStage.qmlrenamed+146−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,18 @@ Item {
3131
3232 property var greeter: { fullyShown: true }
3333
34− PhoneStage {
35− id: phoneStage
34+ ApplicationMenuDataLoader { }
35+
36+ Stage {
37+ id: stage
3638 anchors { fill: parent; rightMargin: units.gu(30) }
3739 focus: true
3840 dragAreaWidth: units.gu(2)
39− maximizedAppTopMargin: units.gu(3)
4041 interactive: true
4142 shellOrientation: Qt.PortraitOrientation
4243 orientations: Orientations {}
4344 applicationManager: ApplicationManager
45+ mode: "staged"
4446 topLevelSurfaceList: TopLevelSurfaceList {
4547 id: topLevelSurfaceList
4648 applicationsModel: ApplicationManager
@@ -66,9 +68,9 @@ Item {
6668 id: edgeBarrierControls
6769 text: "Drag here to pull out spread"
6870 backgroundColor: "blue"
69− onDragged: { phoneStage.pushRightEdge(amount); }
71+ onDragged: { stage.pushRightEdge(amount); }
7072 Component.onCompleted: {
71− edgeBarrierControls.target = testCase.findChild(phoneStage, "edgeBarrierController");
73+ edgeBarrierControls.target = testCase.findChild(stage, "edgeBarrierController");
7274 }
7375 }
7476 Repeater {
@@ -84,11 +86,37 @@ Item {
8486 name: "PhoneStage"
8587 when: windowShown
8688
89+ function init() {
90+ // wait until unity8-dash is up and running.
91+ // it's started automatically by ApplicationManager mock implementation
92+ tryCompare(ApplicationManager, "count", 1);
93+ var dashApp = ApplicationManager.findApplication("unity8-dash");
94+ verify(dashApp);
95+ tryCompare(dashApp, "state", ApplicationInfoInterface.Running);
96+ }
97+
98+ function cleanup() {
99+ ApplicationManager.requestFocusApplication("unity8-dash");
100+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
101+ tryCompare(stage, "state", "staged");
102+ waitForRendering(stage);
103+
104+ killApps();
105+ // wait for Stage to stabilize back into its initial state
106+ var appRepeater = findChild(stage, "appRepeater");
107+ tryCompare(appRepeater, "count", 1);
108+ tryCompare(appRepeater.itemAt(0), "x", 0);
109+
110+ stage.shellOrientationAngle = 0;
111+
112+ waitForRendering(stage)
113+ }
114+
87115 function findAppWindowForSurfaceId(surfaceId) {
88− var delegateObjectName = "spreadDelegate_" + surfaceId;
89− var spreadDelegate = findChild(phoneStage, delegateObjectName);
116+ var delegateObjectName = "appDelegate_" + surfaceId;
117+ var spreadDelegate = findChild(stage, delegateObjectName);
90118 if (!spreadDelegate) {
91− console.warn("Failed to find " + delegateObjectName + " in phoneStage ("+phoneStage+")");
119+ console.warn("Failed to find " + delegateObjectName + " in stage");
92120 return null;
93121 }
94122 var appWindow = findChild(spreadDelegate, "appWindow");
@@ -108,33 +136,29 @@ Item {
108136 function addApps(count) {
109137 if (count == undefined) count = 1;
110138 for (var i = 0; i < count; i++) {
139+ var startingAppId = ApplicationManager.availableApplications[ApplicationManager.count];
111140 var appSurfaceId = topLevelSurfaceList.nextId;
112− var app = ApplicationManager.startApplication(ApplicationManager.availableApplications[ApplicationManager.count])
141+ var app = ApplicationManager.startApplication(startingAppId)
113142 tryCompare(app, "state", ApplicationInfoInterface.Running)
114− var spreadView = findChild(phoneStage, "spreadView");
115− tryCompare(spreadView, "contentX", -spreadView.shift);
116143 waitUntilAppSurfaceShowsUp(appSurfaceId);
117− waitForRendering(phoneStage)
144+ waitForRendering(stage)
145+ tryCompare(ApplicationManager, "focusedApplicationId", startingAppId)
118146 }
119147 }
120148
121149 function performEdgeSwipeToShowAppSpread() {
122− var spreadView = findChild(phoneStage, "spreadView");
123−
124− // Keep it inside the PhoneStage otherwise the controls on the right side will
150+ // Keep it inside the Stage otherwise the controls on the right side will
125151 // capture the press thus the "- 2" on startX.
126− var startX = phoneStage.width - 2;
127− var startY = phoneStage.height / 2;
152+ var startX = stage.width - 2;
153+ var startY = stage.height / 2;
128154 var endY = startY;
129− var endX = phoneStage.width / 2;
155+ var endX = stage.width / 2;
130156
131− touchFlick(phoneStage, startX, startY, endX, endY,
157+ touchFlick(stage, startX, startY, endX, endY,
132158 true /* beginTouch */, true /* endTouch */, units.gu(10), 50);
133159
134− tryCompare(spreadView, "phase", 2);
135− tryCompare(spreadView, "flicking", false);
136− tryCompare(spreadView, "moving", false);
137− waitForRendering(phoneStage);
160+ tryCompare(stage, "state", "spread");
161+ waitForRendering(stage);
138162 }
139163
140164 function swipeSurfaceUpwards(surfaceId) {
@@ -163,7 +187,7 @@ Item {
163187
164188 function waitUntilAppDelegateStopsMoving(targetSurfaceId)
165189 {
166− var targetAppDelegate = findChild(phoneStage, "spreadDelegate_" + targetSurfaceId);
190+ var targetAppDelegate = findChild(stage, "appDelegate_" + targetSurfaceId);
167191 verify(targetAppDelegate);
168192 var lastValue = undefined;
169193 do {
@@ -172,79 +196,39 @@ Item {
172196 } while (lastValue != targetAppDelegate.animatedProgress);
173197 }
174198
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−
196199 function test_enterSpread_data() {
197200 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 },
201+ {tag: "<breakPoint (trigger)", progress: .2, cancel: false, endState: "staged", newFocusedIndex: 1 },
202+ {tag: "<breakPoint (cancel)", progress: .2, cancel: true, endState: "staged", newFocusedIndex: 0 },
203+ {tag: ">breakPoint (trigger)", progress: .5, cancel: false, endState: "spread", newFocusedIndex: 0 },
204+ {tag: ">breakPoint (cancel)", progress: .8, cancel: true, endState: "staged", newFocusedIndex: 0 },
206205 ];
207206 }
208207
209208 function test_enterSpread(data) {
210209 addApps(5)
211210
212− var spreadView = findChild(phoneStage, "spreadView");
213−
214− // Keep it inside the PhoneStage otherwise the controls on the right side will
211+ // Keep it inside the Stage otherwise the controls on the right side will
215212 // capture the press thus the "- 2" on startX.
216− var startX = phoneStage.width - 2;
217− var startY = phoneStage.height / 2;
213+ var startX = stage.width - 2;
214+ var startY = stage.height / 2;
218215 var endY = startY;
219− var endX = spreadView.width - (spreadView.width * spreadView[data.positionMarker]) - data.offset
220− - phoneStage.dragAreaWidth;
216+ var endX = stage.width - (stage.width * data.progress) - stage.dragAreaWidth;
221217
222218 var oldFocusedApp = ApplicationManager.get(0);
223219 var newFocusedApp = ApplicationManager.get(data.newFocusedIndex);
224220
225− touchFlick(phoneStage, startX, startY, endX, endY,
221+ touchFlick(stage, startX, startY, endX, endY,
226222 true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
227223
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− }
224+ if (data.cancel) {
225+ touchFlick(stage, endX, endY, endX + units.gu(5), endY,
226+ false /* beginTouch */, true /* endTouch */, units.gu(10), 50);
227+ } else {
228+ touchRelease(stage, endX, endY); }
246229
247− tryCompare(ApplicationManager, "focusedApplicationId", newFocusedApp.appId);
230+ tryCompare(stage, "state", data.endState);
231+ tryCompare(ApplicationManager, "focusedApplicationId", data.endState == "spread" ? oldFocusedApp.appId : newFocusedApp.appId);
248232 }
249233
250234 function test_selectAppFromSpread_data() {
@@ -263,32 +247,26 @@ Item {
263247 function test_selectAppFromSpread(data) {
264248 addApps(data.total)
265249
266− var spreadView = findChild(phoneStage, "spreadView");
267−
268250 performEdgeSwipeToShowAppSpread();
269251
270− tryCompare(spreadView, "phase", 2);
271−
272− var tile = findChild(spreadView, "spreadDelegate_" + topLevelSurfaceList.idAt(data.index));
252+ var tile = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(data.index));
273253 var appId = ApplicationManager.get(data.index).appId;
274254
275− if (tile.mapToItem(spreadView, 0, 0).x > spreadView.width) {
255+ if (tile.mapToItem(stage, 0, 0).x > stage.width - units.gu(3)) {
276256 // Item is not visible... Need to flick the spread
277− var startX = phoneStage.width - units.gu(1);
278− var startY = phoneStage.height / 2;
257+ var startX = stage.width - units.gu(1);
258+ var startY = stage.height / 2;
279259 var endY = startY;
280260 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);
261+ touchFlick(stage, startX, startY, endX, endY, true, true, units.gu(10), 50)
285262 }
286263
287264 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)
265+ var dragArea = findChild(tile, "dragArea");
266+ tryCompare(dragArea, "closeable", true);
267+ mouseClick(stage, tile.mapToItem(stage, 0, 0).x + units.gu(1), stage.height / 2)
290268 tryCompare(ApplicationManager, "focusedApplicationId", appId);
291− tryCompare(spreadView, "phase", 0);
269+ tryCompare(stage, "state", "staged");
292270 }
293271
294272 function test_select_data() {
@@ -302,16 +280,22 @@ Item {
302280 function test_select(data) {
303281 addApps(5);
304282
305− var spreadView = findChild(phoneStage, "spreadView");
306283 var selectedApp = ApplicationManager.get(data.index);
284+ var appRepeater = findChild(stage, "appRepeater");
285+ var selectedAppDeleage = appRepeater.itemAt(data.index);
307286
308287 performEdgeSwipeToShowAppSpread();
309288
310− phoneStage.select(selectedApp.appId);
289+ print("tapping", selectedAppDeleage.appId, selectedAppDeleage.visible)
290+ if (selectedAppDeleage.x > stage.width - units.gu(5)) {
291+ touchFlick(stage, stage.width - units.gu(2), stage.height / 2, units.gu(2), stage.height / 2, true, true, units.gu(2), 10)
292+ }
293+
294+ tap(selectedAppDeleage, 1, 1);
311295
312− tryCompare(spreadView, "contentX", -spreadView.shift);
296+ tryCompare(stage, "state", "staged");
313297
314− compare(ApplicationManager.focusedApplicationId, selectedApp.appId);
298+ tryCompare(ApplicationManager, "focusedApplicationId", selectedApp.appId);
315299 }
316300
317301 function test_backgroundClickCancelsSpread() {
@@ -320,37 +304,14 @@ Item {
320304 var focusedAppId = ApplicationManager.focusedApplicationId;
321305
322306 performEdgeSwipeToShowAppSpread();
307+ tryCompare(stage, "state", "spread");
323308
324− mouseClick(phoneStage, units.gu(1), units.gu(1));
309+ mouseClick(stage, units.gu(1), units.gu(1));
325310
326− // Make sure the spread is in the idle position
327− var spreadView = findChild(phoneStage, "spreadView");
328− tryCompare(spreadView, "contentX", -spreadView.shift);
311+ tryCompare(stage, "state", "staged");
329312
330313 // 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− }
314+ tryCompare(ApplicationManager, "focusedApplicationId", focusedAppId);
354315 }
355316
356317 function test_focusNewTopMostAppAfterFocusedOneClosesItself() {
@@ -370,60 +331,9 @@ Item {
370331 tryCompare(firstApp, "focused", true);
371332 }
372333
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−
424334 function test_leftEdge_data() {
425335 return [
426− { tag: "normal", inSpread: false, leftEdgeDragWidth: units.gu(5), shouldMoveApp: true },
336+ { tag: "normal", inSpread: false, leftEdgeDragWidth: units.gu(20), shouldMoveApp: true },
427337 { tag: "inSpread", inSpread: true, leftEdgeDragWidth: units.gu(5), shouldMoveApp: false }
428338 ]
429339 }
@@ -435,22 +345,24 @@ Item {
435345 performEdgeSwipeToShowAppSpread();
436346 }
437347
438− var focusedDelegate = findChild(phoneStage, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
439− phoneStage.inverseProgress = data.leftEdgeDragWidth;
348+ var focusedDelegate = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(0));
349+ var currentX = focusedDelegate.x;
350+
351+ stage.leftEdgeDragProgress = data.leftEdgeDragWidth;
440352
441− tryCompare(focusedDelegate, "x", data.shouldMoveApp ? data.leftEdgeDragWidth : 0);
353+ tryCompare(focusedDelegate, "x", data.shouldMoveApp ? data.leftEdgeDragWidth : currentX);
442354
443− phoneStage.inverseProgress = 0;
355+ stage.leftEdgeDragProgress = 0;
444356
445− tryCompare(focusedDelegate, "x", 0);
357+ tryCompare(focusedDelegate, "x", currentX);
446358 }
447359
448360 function test_focusedAppIsTheOnlyRunningApp() {
449361 addApps(2);
450362
451− var delegateA = findChild(phoneStage, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
363+ var delegateA = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(0));
452364 verify(delegateA);
453− var delegateB = findChild(phoneStage, "spreadDelegate_" + topLevelSurfaceList.idAt(1));
365+ var delegateB = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(1));
454366 verify(delegateB);
455367
456368 // A is focused and running, B is unfocused and suspended
@@ -461,7 +373,7 @@ Item {
461373
462374 // Switch foreground/focused appp from A to B
463375 performEdgeSwipeToShowAppSpread();
464− phoneStage.select(delegateB.application.appId);
376+ tap(delegateB, 1, 1);
465377
466378 // Now it's the other way round
467379 // A is unfocused and suspended, B is focused and running
@@ -474,15 +386,15 @@ Item {
474386 function test_dashRemainsRunningIfStageIsToldSo() {
475387 addApps(1);
476388
477− var delegateDash = findChild(phoneStage, "spreadDelegate_" + topLevelSurfaceList.idAt(1));
389+ var delegateDash = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(1));
478390 verify(delegateDash);
479391 compare(delegateDash.application.appId, "unity8-dash");
480392
481− var delegateOther = findChild(phoneStage, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
393+ var delegateOther = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(0));
482394 verify(delegateOther);
483395
484396 performEdgeSwipeToShowAppSpread();
485− phoneStage.select("unity8-dash");
397+ tap(delegateDash, 1, 1);
486398
487399 tryCompare(delegateDash, "focus", true);
488400 tryCompare(delegateDash.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
@@ -490,41 +402,40 @@ Item {
490402 compare(delegateOther.application.requestedState, ApplicationInfoInterface.RequestedSuspended);
491403
492404 performEdgeSwipeToShowAppSpread();
493− phoneStage.select(delegateOther.application.appId);
494−
405+ tap(delegateOther, 1, 1);
495406 // The other app gets focused and running but dash is kept running despite being unfocused
407+ tryCompare(delegateOther, "focus", true);
408+ tryCompare(delegateOther.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
496409 tryCompare(delegateDash, "focus", false);
497410 tryCompare(delegateDash.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
498− compare(delegateOther.focus, true);
499− compare(delegateOther.application.requestedState, ApplicationInfoInterface.RequestedRunning);
500411 }
501412
502413 function test_foregroundAppIsSuspendedWhenStageIsSuspended() {
503414 addApps(1);
504415
505− var delegate = findChild(phoneStage, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
416+ var delegate = findChild(stage, "appDelegate_" + topLevelSurfaceList.idAt(0));
506417 verify(delegate);
507418
508419 compare(delegate.focus, true);
509420 compare(delegate.application.requestedState, ApplicationInfoInterface.RequestedRunning);
510421
511− phoneStage.suspended = true;
422+ stage.suspended = true;
512423
513424 tryCompare(delegate.application, "requestedState", ApplicationInfoInterface.RequestedSuspended);
514425
515− phoneStage.suspended = false;
426+ stage.suspended = false;
516427
517428 tryCompare(delegate.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
518429 }
519430
520431 function test_mouseEdgePush() {
521− var spreadView = findChild(phoneStage, "spreadView")
522432 addApps(1);
523− mouseMove(phoneStage, phoneStage.width - 1, units.gu(10));
433+ mouseMove(stage, stage.width - 1, units.gu(10));
524434 for (var i = 0; i < units.gu(10); i++) {
525− phoneStage.pushRightEdge(1);
435+ stage.pushRightEdge(1);
526436 }
527− tryCompare(spreadView, "phase", 2);
437+ mouseMove(stage, stage.width - units.gu(5), units.gu(10));
438+ compare(stage.state, "spread");
528439 }
529440
530441 function test_closeSurfaceOfMultiSurfaceApp() {
@@ -539,19 +450,44 @@ Item {
539450
540451 performEdgeSwipeToShowAppSpread();
541452
542− var appDelegate = findChild(phoneStage, "spreadDelegate_" + surface1Id);
543− verify(appDelegate);
544− tryCompare(appDelegate, "swipeToCloseEnabled", true);
453+ var appDelegate = findChild(stage, "appDelegate_" + surface1Id);
454+ var dragArea = findChild(appDelegate, "dragArea")
455+ verify(dragArea);
456+ tryCompare(dragArea, "closeable", true);
545457
546458 compare(webbrowserApp.surfaceList.count, 2);
547− compare(webbrowserApp.state, ApplicationInfoInterface.Running);
548459
549460 swipeSurfaceUpwards(surface1Id);
550461
551462 // Surface must eventually be gone
552463 tryCompareFunction(function() { return topLevelSurfaceList.indexForId(surface1Id); }, -1);
553464 tryCompare(webbrowserApp.surfaceList, "count", 1);
554− compare(webbrowserApp.state, ApplicationInfoInterface.Running);
465+ }
466+
467+ function test_swipeToClose_data() {
468+ return [
469+ { tag: "closeable", closeable: true },
470+ { tag: "not closeable", closeable: false }
471+ ]
472+ }
473+
474+ function test_swipeToClose(data) {
475+ var surface1Id = topLevelSurfaceList.nextId;
476+ var webbrowserApp = ApplicationManager.startApplication("webbrowser-app");
477+ waitUntilAppSurfaceShowsUp(surface1Id);
478+
479+ performEdgeSwipeToShowAppSpread();
480+
481+ var appDelegate = findChild(stage, "appDelegate_" + surface1Id);
482+ var dragArea = findChild(appDelegate, "dragArea")
483+ verify(dragArea);
484+ dragArea.closeable = data.closeable;
485+
486+ var oldCount = ApplicationManager.count;
487+
488+ swipeSurfaceUpwards(surface1Id);
489+
490+ tryCompare(ApplicationManager, "count", data.closeable ? oldCount - 1 : oldCount);
555491 }
556492
557493 /*
@@ -632,11 +568,10 @@ Item {
632568
633569 performEdgeSwipeToShowAppSpread();
634570
635− {
636− var appDelegate = findChild(phoneStage, "spreadDelegate_" + webbrowserSurfaceId);
637− verify(appDelegate);
638− tryCompare(appDelegate, "swipeToCloseEnabled", true);
639− }
571+ var appDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
572+ var dragArea = findChild(appDelegate, "dragArea");
573+ verify(dragArea);
574+ tryCompare(dragArea, "closeable", true);
640575
641576 swipeSurfaceUpwards(webbrowserSurfaceId);
642577
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+103−238
@@ -24,53 +24,37 @@ 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)
38+ ApplicationMenuDataLoader { }
5039
40+ Stage {
41+ id: stage
42+ anchors { fill: parent; rightMargin: units.gu(30) }
43+ dragAreaWidth: units.gu(2)
44+ interactive: true
45+ shellOrientation: Qt.LandscapeOrientation
46+ nativeWidth: width
47+ nativeHeight: height
48+ orientations: Orientations {
49+ native_: Qt.LandscapeOrientation
50+ primary: Qt.LandscapeOrientation
51+ }
5152 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− }
53+ mode: "stagedWithSideStage"
54+ applicationManager: ApplicationManager
55+ topLevelSurfaceList: TopLevelSurfaceList {
56+ id: topLevelSurfaceList
57+ applicationsModel: ApplicationManager
7458 }
7559 }
7660
@@ -84,7 +68,7 @@ Rectangle {
8468 right: parent.right
8569 }
8670
87− // don't let mouse clicks in the controls area disturb the TabletStage behind it
71+ // don't let mouse clicks in the controls area disturb the Stage behind it
8872 MouseArea { anchors.fill: parent }
8973
9074 Column {
@@ -95,15 +79,16 @@ Rectangle {
9579 id: edgeBarrierControls
9680 text: "Drag here to pull out spread"
9781 backgroundColor: "blue"
98− onDragged: { tabletStageLoader.item.pushRightEdge(amount); }
82+ onDragged: { stage.pushRightEdge(amount); }
9983 Component.onCompleted: {
100− edgeBarrierControls.target = testCase.findChild(tabletStageLoader, "edgeBarrierController");
84+ edgeBarrierControls.target = testCase.findChild(stage, "edgeBarrierController");
10185 }
10286 }
10387
10488 Button {
10589 text: testCase.sideStage ? testCase.sideStage.shown ? "Hide Side-stage" : "Show Side-stage" : ""
10690 enabled: testCase.sideStage
91+ activeFocusOnPress: false
10792 onClicked: {
10893 if (testCase.sideStage.shown) testCase.sideStage.hide();
10994 else testCase.sideStage.show();
@@ -140,46 +125,50 @@ Rectangle {
140125 name: "TabletStage"
141126 when: windowShown
142127
143− property Item tabletStage: tabletStageLoader.status === Loader.Ready ? tabletStageLoader.item : null
144− property Item sideStage: tabletStage ? findChild(tabletStage, "sideStage") : null
128+ readonly property alias topSurfaceList: stage.topLevelSurfaceList
129+ property Item sideStage: stage ? findChild(stage, "sideStage") : null
145130
146131 function init() {
147132 stageSaver.clear();
148− tabletStageLoader.active = true;
149− tryCompare(tabletStageLoader, "status", Loader.Ready);
150133
151134 tryCompare(topSurfaceList, "count", 1);
152135 compare(topSurfaceList.applicationAt(0).appId, "unity8-dash");
153136
154137 // this is very strange, but sometimes the test starts without
155− // TabletStage components having finished loading themselves
138+ // Stage components having finished loading themselves
156139 var appWindow = null
157140 while (!appWindow) {
158141 appWindow = findAppWindowForSurfaceId(topSurfaceList.idAt(0));
159142 if (!appWindow) {
160− console.log("didn't find unity8-dash appWindow in " + tabletStage + ". Trying again...");
143+ console.log("didn't find unity8-dash appWindow in Stage. Trying again...");
161144 wait(50);
162145 }
163146 }
164147
165148 waitUntilAppSurfaceShowsUp(topSurfaceList.idAt(0));
166149 sideStage.hideNow()
150+ tryCompare(sideStage, "x", stage.width)
167151 }
168152
169153 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);
154+ ApplicationManager.requestFocusApplication("unity8-dash");
155+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
156+ tryCompare(stage, "state", "stagedWithSideStage");
157+ waitForRendering(stage);
180158
181159 killApps();
160+
161+ // wait for Stage to stabilize back into its initial state
162+ var appRepeater = findChild(stage, "appRepeater");
163+ tryCompare(appRepeater, "count", 1);
164+ tryCompare(appRepeater.itemAt(0), "x", 0);
165+
166+ sideStage.hideNow();
167+ tryCompare(sideStage, "x", stage.width)
168+ waitForRendering(stage)
169+
182170 WindowStateStorage.clear();
171+ wait(100)
183172 }
184173
185174 function waitUntilAppSurfaceShowsUp(surfaceId) {
@@ -191,7 +180,7 @@ Rectangle {
191180 }
192181
193182 function findAppWindowForSurfaceId(surfaceId) {
194− var spreadDelegate = findChild(tabletStage, "spreadDelegate_" + surfaceId);
183+ var spreadDelegate = findChild(stage, "appDelegate_" + surfaceId);
195184 if (!spreadDelegate) {
196185 return null;
197186 }
@@ -210,7 +199,7 @@ Rectangle {
210199
211200 function waitUntilAppDelegateStopsMoving(targetSurfaceId)
212201 {
213− var targetAppDelegate = findChild(tabletStage, "spreadDelegate_" + targetSurfaceId);
202+ var targetAppDelegate = findChild(stage, "appDelegate_" + targetSurfaceId);
214203 verify(targetAppDelegate);
215204 var lastValue = undefined;
216205 do {
@@ -220,15 +209,9 @@ Rectangle {
220209 }
221210
222211 function performEdgeSwipeToShowAppSpread() {
223− touchFlick(tabletStage,
224− tabletStage.width - (tabletStage.dragAreaWidth / 2), tabletStage.height / 2,
225− tabletStage.x + 1, tabletStage.height / 2);
212+ touchFlick(stage, stage.width - (stage.dragAreaWidth / 2), stage.height / 2, stage.x + 1, stage.height / 2);
226213
227− var spreadView = findChild(tabletStage, "spreadView");
228− verify(spreadView);
229− tryCompare(spreadView, "phase", 2);
230− tryCompare(spreadView, "flicking", false);
231− tryCompare(spreadView, "moving", false);
214+ tryCompare(stage, "state", "spread");
232215 }
233216
234217 function swipeSurfaceUpwards(surfaceId) {
@@ -244,17 +227,12 @@ Rectangle {
244227
245228 function dragToSideStage(surfaceId) {
246229 sideStage.showNow();
247− var targetAppDelegate = findChild(tabletStage, "spreadDelegate_" + surfaceId);
230+ var targetAppDelegate = findChild(stage, "appDelegate_" + surfaceId);
248231
249− var pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
250− var end_pos = tabletStage.width - sideStage.width / 2;
232+ var pos = stage.width - sideStage.width - (stage.width - sideStage.width) / 2;
233+ var end_pos = stage.width - sideStage.width / 2;
251234
252− multiTouchDragUntil([0,1,2],
253− tabletStage,
254− pos,
255− tabletStage.height / 2,
256− units.gu(3),
257− 0,
235+ multiTouchDragUntil([0,1,2], stage, pos, stage.height / 2, units.gu(3), 0,
258236 function() {
259237 pos += units.gu(3);
260238 return sideStage.shown && !sideStage.showAnimation.running &&
@@ -265,16 +243,16 @@ Rectangle {
265243
266244 function dragToMainStage(surfaceId) {
267245 sideStage.showNow();
268− var targetAppDelegate = findChild(tabletStage, "spreadDelegate_" + surfaceId);
246+ var targetAppDelegate = findChild(stage, "appDelegate_" + surfaceId);
269247 verify(targetAppDelegate);
270248
271− var pos = tabletStage.width - sideStage.width / 2;
272− var end_pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
249+ var pos = stage.width - sideStage.width / 2;
250+ var end_pos = stage.width - sideStage.width - (stage.width - sideStage.width) / 2;
273251
274252 multiTouchDragUntil([0,1,2],
275− tabletStage,
253+ stage,
276254 pos,
277− tabletStage.height / 2,
255+ stage.height / 2,
278256 -units.gu(3),
279257 0,
280258 function() {
@@ -290,7 +268,7 @@ Rectangle {
290268 var webbrowserSurfaceId = topSurfaceList.nextId;
291269 webbrowserCheckBox.checked = true;
292270 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
293− var webbrowserDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
271+ var webbrowserDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
294272 verify(webbrowserDelegate);
295273 compare(webbrowserDelegate.stage, ApplicationInfoInterface.MainStage);
296274 var webbrowserWindow = findAppWindowForSurfaceId(webbrowserSurfaceId);
@@ -303,7 +281,7 @@ Rectangle {
303281 waitUntilAppSurfaceShowsUp(dialerSurfaceId);
304282
305283 var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
306− var dialerDelegate = findChild(tabletStage, "spreadDelegate_" + dialerSurfaceId);
284+ var dialerDelegate = findChild(stage, "appDelegate_" + dialerSurfaceId);
307285 verify(dialerDelegate);
308286 compare(dialerDelegate.stage, ApplicationInfoInterface.SideStage);
309287
@@ -336,17 +314,18 @@ Rectangle {
336314
337315 performEdgeSwipeToShowAppSpread();
338316
339− var appDelegate = findChild(tabletStage, "spreadDelegate_" + dialerSurfaceId);
340− verify(appDelegate);
317+ var appDelegate = findChild(stage, "appDelegate_" + dialerSurfaceId);
318+ var dragArea = findChild(appDelegate, "dragArea");
319+ verify(dragArea);
341320 compare(appDelegate.stage, ApplicationInfoInterface.SideStage);
342− tryCompare(appDelegate, "swipeToCloseEnabled", true);
321+ tryCompare(dragArea, "closeable", true);
343322
344323 swipeSurfaceUpwards(dialerSurfaceId);
345324
346325 // Check that dialer-app has been closed
347326
348327 tryCompareFunction(function() {
349− return findChild(tabletStage, "appWindow_" + dialerCheckBox.appId);
328+ return findChild(stage, "appWindow_" + dialerCheckBox.appId);
350329 }, null);
351330
352331 tryCompareFunction(function() {
@@ -359,7 +338,7 @@ Rectangle {
359338 webbrowserCheckBox.checked = true;
360339 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
361340 var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
362− var webbrowserDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
341+ var webbrowserDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
363342 compare(webbrowserDelegate.stage, ApplicationInfoInterface.MainStage);
364343
365344 tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Running);
@@ -368,7 +347,7 @@ Rectangle {
368347 galleryCheckBox.checked = true;
369348 waitUntilAppSurfaceShowsUp(gallerySurfaceId);
370349 var galleryApp = ApplicationManager.findApplication(galleryCheckBox.appId);
371− var galleryDelegate = findChild(tabletStage, "spreadDelegate_" + gallerySurfaceId);
350+ var galleryDelegate = findChild(stage, "appDelegate_" + gallerySurfaceId);
372351 compare(galleryDelegate.stage, ApplicationInfoInterface.MainStage);
373352
374353 tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
@@ -389,7 +368,7 @@ Rectangle {
389368 WindowStateStorage.saveStage(facebookCheckBox.appId, ApplicationInfoInterface.SideStage)
390369 WindowStateStorage.saveStage(dialerCheckBox.appId, ApplicationInfoInterface.SideStage)
391370
392− var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
371+ var stagesPriv = findInvisibleChild(stage, "DesktopStagePrivate");
393372 verify(stagesPriv);
394373
395374 // launch two main stage apps
@@ -398,7 +377,7 @@ Rectangle {
398377 var webbrowserSurfaceId = topSurfaceList.nextId;
399378 webbrowserCheckBox.checked = true;
400379 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
401− var webbrowserDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
380+ var webbrowserDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
402381 verify(webbrowserDelegate);
403382 compare(webbrowserDelegate.stage, ApplicationInfoInterface.MainStage);
404383 var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
@@ -407,7 +386,7 @@ Rectangle {
407386 galleryCheckBox.checked = true;
408387 waitUntilAppSurfaceShowsUp(gallerySurfaceId);
409388 var galleryApp = ApplicationManager.findApplication(galleryCheckBox.appId);
410− var galleryDelegate = findChild(tabletStage, "spreadDelegate_" + gallerySurfaceId);
389+ var galleryDelegate = findChild(stage, "appDelegate_" + gallerySurfaceId);
411390 compare(galleryDelegate.stage, ApplicationInfoInterface.MainStage);
412391
413392 compare(stagesPriv.mainStageAppId, galleryCheckBox.appId);
@@ -419,14 +398,14 @@ Rectangle {
419398 dialerCheckBox.checked = true;
420399 waitUntilAppSurfaceShowsUp(dialerSurfaceId);
421400 var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
422− var dialerDelegate = findChild(tabletStage, "spreadDelegate_" + dialerSurfaceId);
401+ var dialerDelegate = findChild(stage, "appDelegate_" + dialerSurfaceId);
423402 compare(dialerDelegate.stage, ApplicationInfoInterface.SideStage);
424403
425404 var facebookSurfaceId = topSurfaceList.nextId;
426405 facebookCheckBox.checked = true;
427406 waitUntilAppSurfaceShowsUp(facebookSurfaceId);
428407 var facebookApp = ApplicationManager.findApplication(facebookCheckBox.appId);
429− var facebookDelegate = findChild(tabletStage, "spreadDelegate_" + facebookSurfaceId);
408+ var facebookDelegate = findChild(stage, "appDelegate_" + facebookSurfaceId);
430409 compare(facebookDelegate.stage, ApplicationInfoInterface.SideStage);
431410
432411 compare(stagesPriv.sideStageAppId, facebookCheckBox.appId);
@@ -461,40 +440,31 @@ Rectangle {
461440 webbrowserCheckBox.checked = true;
462441 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
463442 var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
464− var webbrowserDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
443+ var webbrowserDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
465444 compare(webbrowserDelegate.stage, ApplicationInfoInterface.MainStage);
466445
467446 var dialerSurfaceId = topSurfaceList.nextId;
468447 dialerCheckBox.checked = true;
469448 waitUntilAppSurfaceShowsUp(dialerSurfaceId);
470449 var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
471− var dialerDelegate = findChild(tabletStage, "spreadDelegate_" + dialerSurfaceId);
450+ var dialerDelegate = findChild(stage, "appDelegate_" + dialerSurfaceId);
472451 compare(dialerDelegate.stage, ApplicationInfoInterface.SideStage);
473452
474453
475454 compare(webbrowserApp.requestedState, ApplicationInfoInterface.RequestedRunning);
476455 compare(dialerApp.requestedState, ApplicationInfoInterface.RequestedRunning);
477456
478− tabletStage.suspended = true;
457+ stage.suspended = true;
479458
480459 tryCompare(webbrowserApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
481460 tryCompare(dialerApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
482461
483− tabletStage.suspended = false;
462+ stage.suspended = false;
484463
485464 tryCompare(webbrowserApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
486465 tryCompare(dialerApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
487466 }
488467
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−
498468 function test_threeFingerTapOpensSideStage_data() {
499469 return [
500470 { tag: "1 finger", touchIds: [0], result: false },
@@ -505,7 +475,7 @@ Rectangle {
505475 }
506476
507477 function test_threeFingerTapOpensSideStage(data) {
508− multiTouchTap(data.touchIds, tabletStage, tabletStage.width / 2, tabletStage.height / 2);
478+ multiTouchTap(data.touchIds, stage, stage.width / 2, stage.height / 2);
509479 wait(200);
510480 tryCompare(sideStage, "shown", data.result);
511481 }
@@ -523,18 +493,13 @@ Rectangle {
523493 function test_threeFingerTapClosesSideStage(data) {
524494 sideStage.showNow();
525495
526− multiTouchTap(data.touchIds, tabletStage, tabletStage.width / 2, tabletStage.height / 2);
496+ multiTouchTap(data.touchIds, stage, stage.width / 2, stage.height / 2);
527497 wait(200);
528498 tryCompare(sideStage, "shown", data.result);
529499 }
530500
531501 function test_threeFingerDragOpensSidestage() {
532− multiTouchDragUntil([0,1,2],
533− tabletStage,
534− tabletStage.width / 4,
535− tabletStage.height / 4,
536− units.gu(1),
537− 0,
502+ multiTouchDragUntil([0,1,2], stage, stage.width / 4, stage.height / 4, units.gu(1), 0,
538503 function() { return sideStage.shown; });
539504 }
540505
@@ -546,7 +511,7 @@ Rectangle {
546511 }
547512
548513 function test_applicationLoadsInDefaultStage(data) {
549− var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
514+ var stagesPriv = findInvisibleChild(stage, "DesktopStagePrivate");
550515 verify(stagesPriv);
551516
552517 tryCompare(stagesPriv, "mainStageAppId", "unity8-dash");
@@ -570,7 +535,7 @@ Rectangle {
570535 function test_applicationLoadsInSavedStage(data) {
571536 WindowStateStorage.saveStage(webbrowserCheckBox.appId, data.stage)
572537
573− var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
538+ var stagesPriv = findInvisibleChild(stage, "DesktopStagePrivate");
574539 verify(stagesPriv);
575540
576541 tryCompare(stagesPriv, "mainStageAppId", "unity8-dash");
@@ -595,7 +560,7 @@ Rectangle {
595560 WindowStateStorage.saveStage(webbrowserCheckBox.appId, data.fromStage);
596561 stageSaver.clear();
597562
598− var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
563+ var stagesPriv = findInvisibleChild(stage, "DesktopStagePrivate");
599564 verify(stagesPriv);
600565
601566 tryCompare(stagesPriv, "mainStageAppId", "unity8-dash");
@@ -618,67 +583,38 @@ Rectangle {
618583
619584 function test_loadSideStageByDraggingFromMainStage() {
620585 sideStage.showNow();
586+ print("sidestage now shown. launching browser")
621587 var webbrowserSurfaceId = topSurfaceList.nextId;
622588 webbrowserCheckBox.checked = true;
623589 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
624590
625− var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
591+ var appDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
626592 verify(appDelegate);
593+ waitForRendering(stage);
627594 compare(appDelegate.stage, ApplicationInfoInterface.MainStage);
628595
629596 dragToSideStage(webbrowserSurfaceId);
630597
631− var spreadView = findChild(tabletStageLoader, "spreadView")
632− tryCompare(spreadView, "surfaceDragging", false);
633598 tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
634599 }
635600
636− function test_unloadSideStageByDraggingFromStageStage() {
601+ function test_unloadSideStageByDraggingFromSideStage() {
637602 sideStage.showNow();
638603 WindowStateStorage.saveStage(webbrowserCheckBox.appId, ApplicationInfoInterface.SideStage)
639604 var webbrowserSurfaceId = topSurfaceList.nextId;
640605 webbrowserCheckBox.checked = true;
641606 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
642607
643− var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
608+ var appDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
644609 verify(appDelegate);
610+ waitForRendering(stage);
645611 compare(appDelegate.stage, ApplicationInfoInterface.SideStage);
646612
647613 dragToMainStage(webbrowserSurfaceId);
648614
649− var spreadView = findChild(tabletStageLoader, "spreadView")
650− tryCompare(spreadView, "surfaceDragging", false);
651615 tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
652616 }
653617
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−
682618 /*
683619 1- Suspended app gets killed behind the scenes, causing its surface to go zombie.
684620 2- Surface gets screenshotted and removed. Its slot in the topSurfaceList remains,
@@ -724,64 +660,12 @@ Rectangle {
724660 compare(webbrowserApp.surfaceList.count, 1);
725661 }
726662
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−
779663 function test_draggingSurfaceKeepsSurfaceFocus() {
780664 var webbrowserSurfaceId = topSurfaceList.nextId;
781665 webbrowserCheckBox.checked = true;
782666 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
783667
784− var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
668+ var appDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
785669 verify(appDelegate);
786670 compare(appDelegate.stage, ApplicationInfoInterface.MainStage);
787671
@@ -789,9 +673,8 @@ Rectangle {
789673
790674 dragToSideStage(webbrowserSurfaceId);
791675
792− var spreadView = findChild(tabletStageLoader, "spreadView")
793− tryCompare(spreadView, "surfaceDragging", false);
794676 tryCompare(appDelegate.surface, "activeFocus", true);
677+ tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
795678 }
796679
797680 function test_dashDoesNotDragToSidestage() {
@@ -799,59 +682,41 @@ Rectangle {
799682 compare(topSurfaceList.applicationAt(0).appId, "unity8-dash");
800683 var dashSurfaceId = topSurfaceList.idAt(0);
801684
802− var appDelegate = findChild(tabletStage, "spreadDelegate_" + dashSurfaceId);
685+ var appDelegate = findChild(stage, "appDelegate_" + dashSurfaceId);
803686 verify(appDelegate);
804687 compare(appDelegate.stage, ApplicationInfoInterface.MainStage);
805688
806− var pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
807− var end_pos = tabletStage.width - sideStage.width / 2;
689+ var pos = stage.width - sideStage.width - (stage.width - sideStage.width) / 2;
690+ var end_pos = stage.width - sideStage.width / 2;
808691
809− multiTouchDragUntil([0,1,2],
810− tabletStage,
811− pos,
812− tabletStage.height / 2,
813− units.gu(3),
814− 0,
692+ multiTouchDragUntil([0,1,2], stage, pos, stage.height / 2, units.gu(3), 0,
815693 function() {
816694 pos += units.gu(3);
817695 return sideStage.shown && !sideStage.showAnimation.running &&
818696 pos >= end_pos;
819697 });
820698
821− var spreadView = findChild(tabletStageLoader, "spreadView")
822− tryCompare(spreadView, "surfaceDragging", false);
699+ waitForRendering(stage);
823700 tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
824701 }
825702
826− function test_switchStageOnRotation() {
703+ function test_switchRestoreStageOnRotation() {
827704 WindowStateStorage.saveStage(webbrowserCheckBox.appId, ApplicationInfoInterface.SideStage)
828705 var webbrowserSurfaceId = topSurfaceList.nextId;
829706 webbrowserCheckBox.checked = true;
830707 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
831708
832− var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
709+ var appDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
833710 verify(appDelegate);
834711 compare(appDelegate.stage, ApplicationInfoInterface.SideStage);
835712
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−
848713 dragToSideStage(webbrowserSurfaceId);
849714
850715 // will be in sidestage now
851− tabletStage.shellOrientation = Qt.PortraitOrientation;
716+ stage.shellOrientation = Qt.PortraitOrientation;
852717 tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
853718
854− tabletStage.shellOrientation = Qt.LandscapeOrientation;
719+ stage.shellOrientation = Qt.LandscapeOrientation;
855720 tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
856721 }
857722
@@ -860,25 +725,25 @@ Rectangle {
860725 webbrowserCheckBox.checked = true;
861726 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
862727
863− var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
728+ var appDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
864729 verify(appDelegate);
865730
866731 dragToSideStage(webbrowserSurfaceId);
867732 // will be in sidestage now
868− tabletStage.shellOrientation = Qt.PortraitOrientation;
733+ stage.shellOrientation = Qt.PortraitOrientation;
869734 tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
870735
871736 webbrowserCheckBox.checked = false;
872737 tryCompare(ApplicationManager, "count", 1);
873738
874739 // back to landscape
875− tabletStage.shellOrientation = Qt.LandscapeOrientation;
740+ stage.shellOrientation = Qt.LandscapeOrientation;
876741
877742 webbrowserSurfaceId = topSurfaceList.nextId;
878743 webbrowserCheckBox.checked = true;
879744 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
880745
881− appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
746+ appDelegate = findChild(stage, "appDelegate_" + webbrowserSurfaceId);
882747 verify(appDelegate);
883748 tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
884749 }
tests/qmltests/Stages/tst_WindowDecoration.qml → tests/qmltests/Stage/tst_WindowDecoration.qmlrenamed+4−3
@@ -24,7 +24,8 @@ import Unity.Test 0.1
2424import Utils 0.1
2525import QMenuModel 0.1
2626
27−import "../../../qml/Stages"
27+import ".."
28+import "../../../qml/Stage"
2829
2930Item {
3031 id: root
@@ -46,7 +47,7 @@ Item {
4647 property string name: "webbrowser"
4748 }
4849
49− DesktopMenuData { id: desktopMenuData }
50+ ApplicationMenuDataLoader { id: menuData }
5051
5152 Rectangle {
5253 anchors {
@@ -77,7 +78,7 @@ Item {
7778 menu: menuBackend
7879 UnityMenuModel {
7980 id: menuBackend
80− modelData: desktopMenuData.testData
81+ modelData: menuData.generateTestData(5, 3, 3, "menu")
8182 onActivated: log.text = "Activated " + action + "\n" + log.text
8283 }
8384
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/Stages/DesktopMenuData.qmldeleted−549
@@ -1,549 +0,0 @@
1−import QtQuick 2.4
2−
3−QtObject {
4−
5− property var dialerData: [{
6− "rowData": { // 1.1
7− "label": "&dialer1",
8− "action": "dialer1"
9− },
10− "submenu": [{
11− "rowData": { // 1.1
12− "label": "&menu1.1",
13− "action": "menu1.1",
14− "shortcut": "Ctrl+m"
15− }}, {
16− "rowData": { // 1.2
17− "label": "m&enu1.2",
18− "action": "menu1.2",
19− "shortcut": "Ctrl+e"
20− },
21− "submenu": [{
22− "rowData": { // 1.2.1
23− "label": "me&nu1.2.1",
24− "action": "menu1.2.1"
25− }}, {
26− "rowData": { // 1.2.2
27− "label": "menu1.2.2",
28− "action": "menu1.2.2"
29− }}, {
30− "rowData": { // 1.2.3
31− "isSeparator": true
32− }}, {
33− "rowData": { // row 1.2.4
34− "label": "menu1.2.4",
35− "action": "menu1.2.4",
36− "isToggled": true
37− }}, {
38− "rowData": { // row 1.2.5
39− "label": "menu1.2.5",
40− "sensitive": false,
41− }}
42− ]}, {
43− "rowData": { // 1.3
44− "sensitive": false,
45− "isSeparator": true
46− }}, {
47− "rowData": { // row 1.4
48− "label": "menu1.4",
49− "action": "menu1.4",
50− "shortcut": "Ctrl+n"
51− }}
52− ]}, {
53− "rowData": { // 2
54− "label": "d&ialer2",
55− "action": "dialer2"
56− },
57− "submenu": [{
58− "rowData": { // 2.1
59− "label": "menu2.1",
60− "action": "menu2.1"
61− }}
62− ]}, {
63− "rowData": { // row 3
64− "label": "di&aler3",
65− "action": "dialer3"
66− },
67− "submenu": [{
68− "rowData": { // 3.1
69− "label": "menu3.1",
70− "action": "menu3.1"
71− }}
72− ]}
73− ]
74−
75− property var cameraData: [{
76− "rowData": { // 1.1
77− "label": "camera1",
78− "sensitive": true,
79− "isSeparator": false,
80− "icon": "",
81− "ext": {},
82− "action": "camera1",
83− "actionState": {},
84− "isCheck": false,
85− "isRadio": false,
86− "isToggled": false,
87− "shortcut": ""
88− },
89− "submenu": [{
90− "rowData": { // 1.1
91− "label": "menu1.1",
92− "sensitive": true,
93− "isSeparator": false,
94− "icon": "",
95− "ext": {},
96− "action": "menu1.1",
97− "actionState": {},
98− "isCheck": false,
99− "isRadio": false,
100− "isToggled": false,
101− "shortcut": ""
102− }}, {
103− "rowData": { // 1.2
104− "label": "menu1.2",
105− "sensitive": true,
106− "isSeparator": false,
107− "icon": "",
108− "ext": {},
109− "action": "menu1.2",
110− "actionState": {},
111− "isCheck": false,
112− "isRadio": false,
113− "isToggled": false,
114− "shortcut": ""
115− }}, {
116− "rowData": { // row 1.2
117− "label": "menu1.2",
118− "sensitive": true,
119− "isSeparator": false,
120− "icon": "",
121− "ext": {},
122− "action": "menu1.2",
123− "actionState": {},
124− "isCheck": false,
125− "isRadio": false,
126− "isToggled": false,
127− "shortcut": ""
128− }}
129− ]}, {
130− "rowData": { // 2
131− "label": "camera2",
132− "sensitive": true,
133− "isSeparator": false,
134− "icon": "",
135− "ext": {},
136− "action": "camera2",
137− "actionState": {},
138− "isCheck": false,
139− "isRadio": false,
140− "isToggled": false,
141− "shortcut": ""
142− }}
143− ]
144−
145− property var galleryData: [{
146− "rowData": { // 1.1
147− "label": "gallery1",
148− "sensitive": true,
149− "isSeparator": false,
150− "icon": "",
151− "ext": {},
152− "action": "gallery1",
153− "actionState": {},
154− "isCheck": false,
155− "isRadio": false,
156− "isToggled": false,
157− "shortcut": ""
158− },
159− "submenu": [{
160− "rowData": { // 1.1
161− "label": "menu0",
162− "sensitive": true,
163− "isSeparator": false,
164− "icon": "",
165− "ext": {},
166− "action": "menu0",
167− "actionState": {},
168− "isCheck": false,
169− "isRadio": false,
170− "isToggled": false,
171− "shortcut": ""
172− }}, {
173− "rowData": { // 1.2
174− "label": "menu1",
175− "sensitive": true,
176− "isSeparator": false,
177− "icon": "",
178− "ext": {},
179− "action": "menu1",
180− "actionState": {},
181− "isCheck": false,
182− "isRadio": false,
183− "isToggled": false,
184− "shortcut": ""
185− }}, {
186− "rowData": { // 1.2
187− "label": "",
188− "sensitive": false,
189− "isSeparator": true,
190− "icon": "",
191− "type": "",
192− "ext": {},
193− "action": "",
194− "actionState": {},
195− "isCheck": false,
196− "isRadio": false,
197− "isToggled": false,
198− "shortcut": ""
199− }}, {
200− "rowData": { // row 1.2
201− "label": "menu2",
202− "sensitive": true,
203− "isSeparator": false,
204− "icon": "",
205− "ext": {},
206− "action": "menu2",
207− "actionState": {},
208− "isCheck": false,
209− "isRadio": false,
210− "isToggled": false,
211− "shortcut": ""
212− }}
213− ]}, {
214− "rowData": { // 2
215− "label": "gallery2",
216− "sensitive": true,
217− "isSeparator": false,
218− "icon": "",
219− "ext": {},
220− "action": "gallery2",
221− "actionState": {},
222− "isCheck": false,
223− "isRadio": false,
224− "isToggled": false,
225− "shortcut": ""
226− }}, {
227− "rowData": { // row 2
228− "label": "gallery3",
229− "sensitive": true,
230− "isSeparator": false,
231− "icon": "",
232− "ext": {},
233− "action": "gallery3",
234− "actionState": {},
235− "isCheck": false,
236− "isRadio": false,
237− "isToggled": false,
238− "shortcut": ""
239− }}
240− ]
241−
242− property var testData: [{
243− "rowData": { // 1
244− "label": "_menu1",
245− "sensitive": true,
246− "isSeparator": false,
247− "icon": "",
248− "ext": {},
249− "action": "menu1",
250− "actionState": {},
251− "isCheck": false,
252− "isRadio": false,
253− "isToggled": false,
254− "shortcut": "Alt+F"
255− },
256− "submenu": [{
257− "rowData": { // 1.1
258− "label": "menu1.1",
259− "sensitive": true,
260− "isSeparator": false,
261− "icon": "",
262− "ext": {},
263− "action": "menu1.1",
264− "actionState": {},
265− "isCheck": false,
266− "isRadio": false,
267− "isToggled": false,
268− "shortcut": "Alt+0"
269− }}, {
270− "rowData": { // 1.2
271− "label": "m_enu1.2",
272− "sensitive": true,
273− "isSeparator": false,
274− "icon": "",
275− "ext": {},
276− "action": "menu1.2",
277− "actionState": {},
278− "isCheck": false,
279− "isRadio": false,
280− "isToggled": false,
281− "shortcut": "Alt+1"
282− },
283− "submenu": [{
284− "rowData": { // 1.2.1
285− "label": "menu1.2.1",
286− "sensitive": true,
287− "isSeparator": false,
288− "icon": "",
289− "ext": {},
290− "action": "menu1.2.1",
291− "actionState": {},
292− "isCheck": false,
293− "isRadio": false,
294− "isToggled": false,
295− "shortcut": ""
296− }}, {
297− "rowData": { // 1.2.2
298− "label": "men_u1.2.2",
299− "sensitive": true,
300− "isSeparator": false,
301− "icon": "",
302− "ext": {},
303− "action": "menu1.2.2",
304− "actionState": {},
305− "isCheck": false,
306− "isRadio": false,
307− "isToggled": false,
308− "shortcut": ""
309− }}, {
310− "rowData": { // 1.2.3
311− "label": "",
312− "sensitive": false,
313− "isSeparator": true,
314− "icon": "",
315− "type": "",
316− "ext": {},
317− "action": "",
318− "actionState": {},
319− "isCheck": false,
320− "isRadio": false,
321− "isToggled": false,
322− "shortcut": ""
323− }}, {
324− "rowData": { // row 1.2.4
325− "label": "menu1.2.4",
326− "sensitive": true,
327− "isSeparator": false,
328− "icon": "",
329− "ext": {},
330− "action": "menu1.2.4",
331− "actionState": {},
332− "isCheck": false,
333− "isRadio": false,
334− "isToggled": true,
335− "shortcut": ""
336− }}
337− ]}, {
338− "rowData": { // 1.3
339− "label": "",
340− "sensitive": false,
341− "isSeparator": true,
342− "icon": "",
343− "type": "",
344− "ext": {},
345− "action": "",
346− "actionState": {},
347− "isCheck": false,
348− "isRadio": false,
349− "isToggled": false,
350− "shortcut": ""
351− }}, {
352− "rowData": { // row 1.4
353− "label": "menu1.4",
354− "sensitive": true,
355− "isSeparator": false,
356− "icon": "",
357− "ext": {},
358− "action": "menu1.4",
359− "actionState": {},
360− "isCheck": true,
361− "isRadio": false,
362− "isToggled": true,
363− "shortcut": "Alt+2"
364− }}
365− ]}, {
366− "rowData": { // 2
367− "label": "menu2",
368− "sensitive": true,
369− "isSeparator": false,
370− "icon": "",
371− "ext": {},
372− "action": "menu2",
373− "actionState": {},
374− "isCheck": false,
375− "isRadio": false,
376− "isToggled": false,
377− "shortcut": "Alt+E"
378− },
379− "submenu": [{
380− "rowData": { // 2.1
381− "label": "menu2.1",
382− "sensitive": true,
383− "isSeparator": false,
384− "icon": "",
385− "ext": {},
386− "action": "menu2.1",
387− "actionState": {},
388− "isCheck": false,
389− "isRadio": false,
390− "isToggled": false,
391− "shortcut": ""
392− }}
393− ]}, {
394− "rowData": { // row 3
395− "label": "me_nu3",
396− "sensitive": true,
397− "isSeparator": false,
398− "icon": "",
399− "ext": {},
400− "action": "dialer3",
401− "actionState": {},
402− "isCheck": false,
403− "isRadio": false,
404− "isToggled": false,
405− "shortcut": ""
406− },
407− "submenu": [{
408− "rowData": { // 3.1
409− "label": "menu3.1",
410− "sensitive": true,
411− "isSeparator": false,
412− "icon": "",
413− "ext": {},
414− "action": "menu3.1",
415− "actionState": {},
416− "isCheck": false,
417− "isRadio": false,
418− "isToggled": false,
419− "shortcut": ""
420− }}
421− ]}
422− ]
423−
424− property var deepTestData: [{
425− "rowData": { // 1
426− "label": "_menu1",
427− "sensitive": true,
428− "isSeparator": false,
429− "icon": "",
430− "ext": {},
431− "action": "menu1",
432− "actionState": {},
433− "isCheck": false,
434− "isRadio": false,
435− "isToggled": false,
436− "shortcut": "Alt+F"
437− },
438− "submenu": [{
439− "rowData": { // 1.1
440− "label": "menu1.1",
441− "sensitive": true,
442− "isSeparator": false,
443− "icon": "",
444− "ext": {},
445− "action": "menu1.1",
446− "actionState": {},
447− "isCheck": false,
448− "isRadio": false,
449− "isToggled": false,
450− "shortcut": ""
451− },
452− "submenu": [{
453− "rowData": { // 1.1.1
454− "label": "menu1.1.1",
455− "sensitive": true,
456− "isSeparator": false,
457− "icon": "",
458− "ext": {},
459− "action": "menu1.1.1",
460− "actionState": {},
461− "isCheck": false,
462− "isRadio": false,
463− "isToggled": false,
464− "shortcut": ""
465− },
466− "submenu": [{
467− "rowData": { // 1.1.1
468− "label": "menu1.1.1.1",
469− "sensitive": true,
470− "isSeparator": false,
471− "icon": "",
472− "ext": {},
473− "action": "menu1.1.1.1",
474− "actionState": {},
475− "isCheck": false,
476− "isRadio": false,
477− "isToggled": false,
478− "shortcut": ""
479− },
480− "submenu": [{
481− "rowData": { // 1.1.1.1
482− "label": "menu1.1.1.1.1",
483− "sensitive": true,
484− "isSeparator": false,
485− "icon": "",
486− "ext": {},
487− "action": "menu1.1.1.1.1",
488− "actionState": {},
489− "isCheck": false,
490− "isRadio": false,
491− "isToggled": false,
492− "shortcut": ""
493− }}
494− ]}
495− ]}
496− ]}
497− ]}
498− ]
499−
500− property var singleCheckable: [{
501− "rowData": { // 1
502− "label": "checkable1",
503− "sensitive": true,
504− "isSeparator": false,
505− "icon": "",
506− "ext": {},
507− "action": "checkable1",
508− "actionState": {},
509− "isCheck": true,
510− "isRadio": false,
511− "isToggled": false,
512− "shortcut": "Alt+F"
513− }
514− }]
515−
516− function generateTestData(length, depth, separatorInterval, prefix) {
517− var data = [];
518−
519− if (prefix === undefined) prefix = "menu"
520−
521− for (var i = 0; i < length; i++) {
522−
523− var menuCode = String.fromCharCode(i+65);
524−
525− var isSeparator = separatorInterval > 0 && ((i+1) % separatorInterval == 0);
526− var row = {
527− "rowData": { // 1
528− "label": prefix + "&" + menuCode,
529− "sensitive": true,
530− "isSeparator": isSeparator,
531− "icon": "",
532− "ext": {},
533− "action": prefix + menuCode,
534− "actionState": {},
535− "isCheck": false,
536− "isRadio": false,
537− "isToggled": false,
538− "shortcut": ""
539− }
540− }
541− if (!isSeparator && depth > 1) {
542− var submenu = generateTestData(length, depth-1, separatorInterval, prefix + menuCode + ".");
543− row["submenu"] = submenu;
544− }
545− data[i] = row;
546− }
547− return data;
548− }
549−}
tests/qmltests/Stages/tst_DecoratedWindow.qmldeleted−82
@@ -1,82 +0,0 @@
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 Unity.ApplicationMenu 0.1
21−import Unity.Indicators 0.1 as Indicators
22−import Unity.Test 0.1
23−import Utils 0.1
24−import QMenuModel 0.1
25−
26−import "../../../qml/Stages"
27−
28−Item {
29− id: root
30− width: units.gu(70)
31− height: units.gu(50)
32−
33− Component.onCompleted: {
34− QuickUtils.keyboardAttached = true;
35− theme.name = "Ubuntu.Components.Themes.SuruDark"
36−
37− ApplicationMenuRegistry.RegisterSurfaceMenu("dialerAppSurfaceId", "/dialerapp", "/dialerapp", ":2");
38− Indicators.UnityMenuModelCache.setCachedModelData("/dialerapp", appMenuData.dialerData);
39−
40− fakeApplication = ApplicationManager.add("dialer-app");
41− fakeApplication.manualSurfaceCreation = true;
42− fakeApplication.setState(ApplicationInfoInterface.Starting);
43−
44− fakeSurface = SurfaceManager.createSurface("dialerAppSurface",Mir.NormalType, Mir.MaximizedState, fakeApplication);
45− fakeApplication.setState(ApplicationInfoInterface.Running);
46− }
47− property QtObject fakeApplication: null
48− property QtObject fakeSurface: null
49−
50− Binding {
51− target: MouseTouchAdaptor
52− property: "enabled"
53− value: false
54− }
55−
56− DesktopMenuData { id: appMenuData }
57−
58− Item {
59− anchors.fill: parent
60− anchors.margins: units.gu(2)
61−
62− DecoratedWindow {
63− id: window
64− active: true
65− requestedHeight: parent.height
66− requestedWidth: parent.width
67−
68− application: fakeApplication
69− surface: fakeSurface
70− focus: true
71− }
72− }
73−
74− UnityTestCase {
75− id: testCase
76− name: "DecoratedWindow"
77− when: windowShown
78−
79− function init() {
80− }
81− }
82−}
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−172
@@ -38,7 +38,7 @@ import Unity.Indicators 0.1 as Indicators
3838import "../../qml"
3939import "../../qml/Components"
4040import "../../qml/Components/PanelState"
41−import "Stages"
41+import "Stage"
4242
4343Rectangle {
4444 id: root
@@ -51,21 +51,14 @@ Rectangle {
5151 LightDM.Greeter.mockMode = "single";
5252 LightDM.Users.mockMode = "single";
5353 shellLoader.active = true;
54−
55− ApplicationMenuRegistry.RegisterSurfaceMenu("DialerId", "/dialerapp", "/dialerapp", ":0");
56− Indicators.UnityMenuModelCache.setCachedModelData("/dialerapp", appMenuData.dialerData);
57−
58− ApplicationMenuRegistry.RegisterSurfaceMenu("CameraId", "/cameraapp", "/cameraapp", ":1");
59− Indicators.UnityMenuModelCache.setCachedModelData("/cameraapp", appMenuData.cameraData);
60−
61− ApplicationMenuRegistry.RegisterSurfaceMenu("GalleryId", "/galleryapp", "/galleryapp", ":2");
62− Indicators.UnityMenuModelCache.setCachedModelData("/galleryapp", appMenuData.galleryData);
6354 }
6455
6556 DesktopMenuData {
6657 id: appMenuData
6758 }
6859
60+ property var shell: shellLoader.item ? shellLoader.item : null
61+
6962 Item {
7063 id: shellContainer
7164 anchors.left: root.left
@@ -123,6 +116,7 @@ Rectangle {
123116 sourceComponent: Component {
124117 Shell {
125118 id: __shell
119+ objectName: "shell"
126120 usageScenario: usageScenarioSelector.model[usageScenarioSelector.selectedIndex]
127121 nativeWidth: width
128122 nativeHeight: height
@@ -199,22 +193,44 @@ Rectangle {
199193 callManager.foregroundCall = callManager.foregroundCall ? null : phoneCall;
200194 }
201195 }
202− }
203− Button {
204− text: "Show Launcher"
205− activeFocusOnPress: false
206− onClicked: {
207− if (shellLoader.status !== Loader.Ready)
208− return;
196+ Button {
197+ text: "Show Launcher"
198+ activeFocusOnPress: false
199+ onClicked: {
200+ if (shellLoader.status !== Loader.Ready)
201+ return;
209202
210− var launcher = testCase.findChild(shellLoader.item, "launcher");
211− launcher.state = "visible";
203+ var launcher = testCase.findChild(shellLoader.item, "launcher");
204+ launcher.state = "visible";
205+ }
212206 }
207+ Button {
208+ text: "Print focused"
209+ activeFocusOnPress: false
210+ onClicked: {
211+ var childs = new Array(0);
212+ childs.push(shellLoader.item)
213+ while (childs.length > 0) {
214+ if (childs[0].activeFocus && childs[0].focus && childs[0].objectName != "shell") {
215+ console.log("Active focus is on item:", childs[0]);
216+ return;
217+ }
218+ for (var i in childs[0].children) {
219+ childs.push(childs[0].children[i])
220+ }
221+ childs.splice(0, 1);
222+ }
223+ console.log("No active focused item found within shell.")
224+ }
225+ }
226+ }
227+ Label {
228+ text: "LightDM mock mode"
213229 }
230+
214231 ListItem.ItemSelector {
215232 anchors { left: parent.left; right: parent.right }
216233 activeFocusOnPress: false
217− text: "LightDM mock mode"
218234 model: ["single", "single-passphrase", "single-pin", "full"]
219235 onSelectedIndexChanged: {
220236 shellLoader.active = false;
@@ -223,33 +239,41 @@ Rectangle {
223239 shellLoader.active = true;
224240 }
225241 }
242+ Label {
243+ text: "Size"
244+ }
245+
226246 ListItem.ItemSelector {
247+ id: sizeSelector
227248 anchors { left: parent.left; right: parent.right }
228249 activeFocusOnPress: false
229− text: "Size"
230250 model: ["phone", "tablet", "desktop"]
231251 onSelectedIndexChanged: {
232− shellLoader.active = false;
233252 shellLoader.state = model[selectedIndex];
234− shellLoader.active = true;
235253 }
236254 }
255+ Label {
256+ text: "Usage scenario"
257+ }
258+
237259 ListItem.ItemSelector {
238260 id: usageScenarioSelector
239261 anchors { left: parent.left; right: parent.right }
240262 activeFocusOnPress: false
241− text: "Usage scenario"
242263 model: ["phone", "tablet", "desktop"]
243264 }
244265 MouseTouchEmulationCheckbox {
245266 id: mouseEmulation
246267 checked: true
247268 }
269+ Label {
270+ text: "Ctrl key as"
271+ }
272+
248273 ListItem.ItemSelector {
249274 id: ctrlModifier
250275 anchors { left: parent.left; right: parent.right }
251276 activeFocusOnPress: false
252− text: "Ctrl key as"
253277 model: ["Ctrl", "Alt", "Super"]
254278 onSelectedIndexChanged: {
255279 var keyMapper = testCase.findChild(shellContainer, "physicalKeysMapper");
@@ -276,6 +300,7 @@ Rectangle {
276300 Button {
277301 text: "Start all apps"
278302 width: parent.width
303+ activeFocusOnPress: false
279304 onClicked: {
280305 for (var i = 0; i < ApplicationManager.availableApplications.length; i++) {
281306 var appId = ApplicationManager.availableApplications[i];
@@ -285,7 +310,7 @@ Rectangle {
285310 }
286311
287312 Repeater {
288− id: appRepeater
313+ id: appCheckBoxRepeater
289314 model: ApplicationManager.availableApplications
290315 ApplicationCheckBox {
291316 appId: modelData
@@ -586,6 +611,38 @@ Rectangle {
586611 compare(ApplicationManager.get(0).appId, "unity8-dash");
587612 }
588613
614+ function test_phoneLeftEdgeDrag_data() {
615+ return [
616+ {tag: "without launcher",
617+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
618+ launcherHides: true, greeterShown: false},
619+
620+ {tag: "with launcher",
621+ revealLauncher: true, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
622+ launcherHides: true, greeterShown: false},
623+
624+ {tag: "small swipe",
625+ revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
626+ launcherHides: false, greeterShown: false},
627+
628+ {tag: "long swipe",
629+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
630+ launcherHides: true, greeterShown: false},
631+
632+ {tag: "small swipe with greeter",
633+ revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
634+ launcherHides: false, greeterShown: true},
635+
636+ {tag: "long swipe with greeter",
637+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
638+ launcherHides: true, greeterShown: true},
639+
640+ {tag: "swipe over dash",
641+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "unity8-dash",
642+ launcherHides: false, greeterShown: false},
643+ ];
644+ }
645+
589646 function test_snapDecisionDismissalReturnsFocus() {
590647 loadShell("phone");
591648 swipeAwayGreeter();
@@ -650,38 +707,6 @@ Rectangle {
650707 mockNotificationsModel.append(n)
651708 }
652709
653− function test_phoneLeftEdgeDrag_data() {
654− return [
655− {tag: "without launcher",
656− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
657− launcherHides: true, greeterShown: false},
658−
659− {tag: "with launcher",
660− revealLauncher: true, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
661− launcherHides: true, greeterShown: false},
662−
663− {tag: "small swipe",
664− revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
665− launcherHides: false, greeterShown: false},
666−
667− {tag: "long swipe",
668− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
669− launcherHides: true, greeterShown: false},
670−
671− {tag: "small swipe with greeter",
672− revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
673− launcherHides: false, greeterShown: true},
674−
675− {tag: "long swipe with greeter",
676− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
677− launcherHides: true, greeterShown: true},
678−
679− {tag: "swipe over dash",
680− revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "unity8-dash",
681− launcherHides: false, greeterShown: false},
682− ];
683− }
684−
685710 function test_phoneLeftEdgeDrag(data) {
686711 loadShell("phone");
687712 swipeAwayGreeter();
@@ -1380,7 +1405,7 @@ Rectangle {
13801405 function test_tapOnRightEdgeReachesApplicationSurface() {
13811406 loadShell("phone");
13821407 swipeAwayGreeter();
1383− var topmostSpreadDelegate = findChild(shell, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
1408+ var topmostSpreadDelegate = findChild(shell, "appDelegate_" + topLevelSurfaceList.idAt(0));
13841409 verify(topmostSpreadDelegate);
13851410
13861411 waitUntilFocusedApplicationIsShowingItsSurface();
@@ -1388,7 +1413,7 @@ Rectangle {
13881413 var topmostSurfaceItem = findChild(topmostSpreadDelegate, "surfaceItem");
13891414 verify(topmostSurfaceItem);
13901415
1391− var rightEdgeDragArea = findChild(shell, "spreadDragArea");
1416+ var rightEdgeDragArea = findChild(shell, "rightEdgeDragArea");
13921417 topmostSurfaceItem.touchPressCount = 0;
13931418 topmostSurfaceItem.touchReleaseCount = 0;
13941419
@@ -1409,9 +1434,9 @@ Rectangle {
14091434 function test_rightEdgeDragDoesNotReachApplicationSurface() {
14101435 loadShell("phone");
14111436 swipeAwayGreeter();
1412− var topmostSpreadDelegate = findChild(shell, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
1437+ var topmostSpreadDelegate = findChild(shell, "appDelegate_" + topLevelSurfaceList.idAt(0));
14131438 var topmostSurfaceItem = findChild(topmostSpreadDelegate, "surfaceItem");
1414− var rightEdgeDragArea = findChild(shell, "spreadDragArea");
1439+ var rightEdgeDragArea = findChild(shell, "rightEdgeDragArea");
14151440
14161441 topmostSurfaceItem.touchPressCount = 0;
14171442 topmostSurfaceItem.touchReleaseCount = 0;
@@ -1429,7 +1454,7 @@ Rectangle {
14291454
14301455 function waitUntilFocusedApplicationIsShowingItsSurface()
14311456 {
1432− var spreadDelegate = findChild(shell, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
1457+ var spreadDelegate = findChild(shell, "appDelegate_" + topLevelSurfaceList.idAt(0));
14331458 var appState = findInvisibleChild(spreadDelegate, "applicationWindowStateGroup");
14341459 tryCompare(appState, "state", "surface");
14351460 var transitions = appState.transitions;
@@ -1448,8 +1473,7 @@ Rectangle {
14481473
14491474 // check if it's indeed showing the spread
14501475 var stage = findChild(shell, "stage");
1451− var spreadView = findChild(stage, "spreadView");
1452− tryCompare(spreadView, "phase", 2);
1476+ tryCompare(stage, "state", "spread");
14531477 }
14541478
14551479 function test_tapUbuntuIconInLauncherOverAppSpread() {
@@ -1485,8 +1509,7 @@ Rectangle {
14851509 // check that the stage has left spread mode.
14861510 {
14871511 var stage = findChild(shell, "stage");
1488− var spreadView = findChild(stage, "spreadView");
1489− tryCompare(spreadView, "phase", 0);
1512+ tryCompare(stage, "state", "staged");
14901513 }
14911514
14921515 // check that the launcher got dismissed
@@ -1553,21 +1576,6 @@ Rectangle {
15531576 }
15541577 }
15551578
1556− function test_stageLoader_data() {
1557− return [
1558− {tag: "phone", source: "Stages/PhoneStage.qml", formFactor: "phone", usageScenario: "phone"},
1559− {tag: "tablet", source: "Stages/TabletStage.qml", formFactor: "tablet", usageScenario: "tablet"},
1560− {tag: "desktop", source: "Stages/DesktopStage.qml", formFactor: "tablet", usageScenario: "desktop"}
1561− ]
1562− }
1563−
1564− function test_stageLoader(data) {
1565− loadShell(data.formFactor);
1566− shell.usageScenario = data.usageScenario;
1567− var stageLoader = findChild(shell, "applicationsDisplayLoader");
1568− verify(String(stageLoader.source).indexOf(data.source) >= 0);
1569− }
1570−
15711579 function test_launcherInverted_data() {
15721580 return [
15731581 {tag: "phone", formFactor: "phone", usageScenario: "phone", launcherInverted: true},
@@ -1681,47 +1689,41 @@ Rectangle {
16811689 function test_altTabWrapAround() {
16821690 loadDesktopShellWithApps();
16831691
1684− var desktopStage = findChild(shell, "stage");
1685− verify(desktopStage !== null)
1686−
1687− var desktopSpread = findChild(shell, "spread");
1688− verify(desktopSpread !== null)
1689−
1690− var spreadContainer = findInvisibleChild(shell, "spreadContainer")
1691− verify(spreadContainer !== null)
1692+ var stage = findChild(shell, "stage");
1693+ verify(stage !== null)
16921694
1693− var spreadRepeater = findInvisibleChild(shell, "spreadRepeater")
1694− verify(spreadRepeater !== null)
1695+ var spread = findChild(shell, "spreadItem");
1696+ verify(spread !== null)
16951697
16961698 // remember the focused appId
16971699 var focused = ApplicationManager.get(ApplicationManager.findApplication(ApplicationManager.focusedApplicationId));
16981700
1699− tryCompare(desktopSpread, "state", "")
1701+ tryCompare(stage, "state", "windowed")
17001702
17011703 // Just press Alt, make sure the spread comes up
17021704 keyPress(Qt.Key_Alt);
17031705 keyClick(Qt.Key_Tab);
1704− tryCompare(desktopSpread, "state", "altTab")
1705− tryCompare(spreadRepeater, "highlightedIndex", 1)
1706+ tryCompare(stage, "state", "spread")
1707+ tryCompare(spread, "highlightedIndex", 1)
17061708 waitForRendering(shell)
17071709
17081710 // Now press and hold Tab, make sure the highlight moves all the way but stops at the last one
17091711 // We can't simulate a pressed key with keyPress() currently, so let's inject the events
17101712 // at API level. Jump for 10 times, verify that it's still at the last one and didn't wrap around.
17111713 for (var i = 0; i < 10; i++) {
1712− desktopSpread.selectNext(true); // true == isAutoRepeat
1714+ spread.selectNext(true); // true == isAutoRepeat
17131715 wait(0); // Trigger the event loop to make sure all the things happen
17141716 }
1715− tryCompare(spreadRepeater, "highlightedIndex", 6)
1717+ tryCompare(spread, "highlightedIndex", 6)
17161718
17171719 // Now release it once, and verify that it does wrap around with an additional Tab press
17181720 keyRelease(Qt.Key_Tab);
17191721 keyClick(Qt.Key_Tab);
1720− tryCompare(spreadRepeater, "highlightedIndex", 0)
1722+ tryCompare(spread, "highlightedIndex", 0)
17211723
17221724 // Release control, check if spread disappears
17231725 keyRelease(Qt.Key_Alt)
1724− tryCompare(desktopSpread, "state", "")
1726+ tryCompare(stage, "state", "windowed")
17251727
17261728 // Make sure that after wrapping around once, we have the same one focused as at the beginning
17271729 var focusedAppSurface = focused.surfaceList.get(0);
@@ -1732,30 +1734,30 @@ Rectangle {
17321734 function test_altBackTabNavigation() {
17331735 loadDesktopShellWithApps();
17341736
1735− var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1736− verify(spreadRepeater !== null);
1737+ var spreadItem = findChild(shell, "spreadItem");
1738+ verify(spreadItem !== null);
17371739
17381740 keyPress(Qt.Key_Alt)
17391741 keyClick(Qt.Key_Tab);
1740− tryCompare(spreadRepeater, "highlightedIndex", 1);
1742+ tryCompare(spreadItem, "highlightedIndex", 1);
17411743
17421744 keyClick(Qt.Key_Tab);
1743− tryCompare(spreadRepeater, "highlightedIndex", 2);
1745+ tryCompare(spreadItem, "highlightedIndex", 2);
17441746
17451747 keyClick(Qt.Key_Tab);
1746− tryCompare(spreadRepeater, "highlightedIndex", 3);
1748+ tryCompare(spreadItem, "highlightedIndex", 3);
17471749
17481750 keyClick(Qt.Key_Tab);
1749− tryCompare(spreadRepeater, "highlightedIndex", 4);
1751+ tryCompare(spreadItem, "highlightedIndex", 4);
17501752
17511753 keyClick(Qt.Key_Backtab);
1752− tryCompare(spreadRepeater, "highlightedIndex", 3);
1754+ tryCompare(spreadItem, "highlightedIndex", 3);
17531755
17541756 keyClick(Qt.Key_Backtab);
1755− tryCompare(spreadRepeater, "highlightedIndex", 2);
1757+ tryCompare(spreadItem, "highlightedIndex", 2);
17561758
17571759 keyClick(Qt.Key_Backtab);
1758− tryCompare(spreadRepeater, "highlightedIndex", 1);
1760+ tryCompare(spreadItem, "highlightedIndex", 1);
17591761
17601762 keyRelease(Qt.Key_Alt);
17611763 }
@@ -1791,21 +1793,24 @@ Rectangle {
17911793 function test_closeFromSpread() {
17921794 loadDesktopShellWithApps()
17931795
1794− var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1795− verify(spreadRepeater !== null);
1796+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1797+ verify(appRepeater !== null);
1798+
1799+ var spreadItem = findChild(shell, "spreadItem");
1800+ verify(spreadItem !== null);
17961801
17971802 keyPress(Qt.Key_Alt)
17981803 keyClick(Qt.Key_Tab);
17991804
18001805 var surfaceId = topLevelSurfaceList.idAt(2);
1801− var spreadDelegate2 = spreadRepeater.itemAt(2);
1806+ var spreadDelegate2 = appRepeater.itemAt(2);
18021807 var closeMouseArea = findChild(spreadDelegate2, "closeMouseArea");
18031808
18041809 // Move the mosue over tile 2 and verify the close button becomes visible
18051810 var x = 0;
18061811 var y = shell.height * .5;
18071812 mouseMove(shell, x, y)
1808− while (spreadRepeater.highlightedIndex !== 2 && x <= 4000) {
1813+ while (spreadItem.highlightedIndex !== 2 && x <= 4000) {
18091814 x+=10;
18101815 mouseMove(shell, x, y)
18111816 wait(0); // spin the loop so bindings get evaluated
@@ -1835,37 +1840,37 @@ Rectangle {
18351840 loadDesktopShellWithApps()
18361841
18371842 var stage = findChild(shell, "stage");
1838− var spread = findChild(stage, "spread");
1839− waitForRendering(spread)
1843+ var spreadItem = findChild(stage, "spreadItem");
1844+// waitForRendering(spread)
18401845
1841− var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1842− verify(spreadRepeater !== null);
1846+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1847+ verify(appRepeater !== null);
18431848
18441849 keyPress(Qt.Key_Alt)
18451850 keyClick(Qt.Key_Tab);
18461851
18471852 var surface = topLevelSurfaceList.surfaceAt(2);
1848− var spreadDelegate2 = spreadRepeater.itemAt(2);
1849− var clippedSpreadDelegate = findChild(spreadDelegate2, "clippedSpreadDelegate");
1853+ var spreadDelegate2 = appRepeater.itemAt(2);
1854+ var decoratedWindow = findChild(spreadDelegate2, "decoratedWindow");
18501855
1851− tryCompare(spread, "state", "altTab");
1856+ tryCompare(stage, "state", "spread");
18521857
18531858 // Move the mouse over tile 2 and verify the highlight becomes visible
18541859 var x = 0;
1855− var y = shell.height * (data.tileInfo ? .95 : 0.5)
1860+ var y = shell.height * (data.tileInfo ? .9 : 0.5)
18561861 mouseMove(shell, x, y)
1857− while (spreadRepeater.highlightedIndex !== 2 && x <= 4000) {
1862+ while (spreadItem.highlightedIndex !== 2 && x <= 4000) {
18581863 x+=10;
18591864 mouseMove(shell, x, y)
18601865 wait(0); // spin the loop so bindings get evaluated
18611866 }
1862− tryCompare(clippedSpreadDelegate, "highlightShown", true);
1867+ tryCompare(decoratedWindow, "showHighlight", true);
18631868
18641869 // Click the tile
1865− mouseClick(clippedSpreadDelegate, clippedSpreadDelegate.width / 2, clippedSpreadDelegate.height / 2)
1870+ mouseClick(decoratedWindow, units.gu(2), decoratedWindow.height / 2)
18661871
18671872 // Verify that we left the spread and app2 is the focused one now
1868− tryCompare(stage, "state", "");
1873+ tryCompare(stage, "state", "windowed");
18691874 tryCompare(surface, "focused", true);
18701875
18711876 keyRelease(Qt.Key_Alt);
@@ -1927,41 +1932,6 @@ Rectangle {
19271932 tryCompare(hoverMouseArea, "visible", false)
19281933 }
19291934
1930− function test_workspacePreviewsHighlightedApp() {
1931− loadDesktopShellWithApps()
1932−
1933− var targetZ = topLevelSurfaceList.count + 1;
1934−
1935− var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1936− verify(spreadRepeater !== null);
1937−
1938− var appRepeater = findInvisibleChild(shell, "appRepeater");
1939− verify(appRepeater !== null);
1940−
1941− keyPress(Qt.Key_Alt)
1942− keyClick(Qt.Key_Tab);
1943−
1944− tryCompare(spreadRepeater, "highlightedIndex", 1);
1945− tryCompare(appRepeater.itemAt(1), "z", targetZ)
1946−
1947− var x = 0;
1948− var y = shell.height * .75;
1949− mouseMove(shell, x, y)
1950−
1951− for (var i = 0; i < 7; i++) {
1952− while (spreadRepeater.highlightedIndex != i && x <= 4000) {
1953− tryCompare(appRepeater.itemAt(spreadRepeater.highlightedIndex), "z", targetZ)
1954− x+=10;
1955− mouseMove(shell, x, y)
1956− wait(0); // spin the loop so bindings get evaluated
1957− }
1958− }
1959−
1960− verify(y < 4000);
1961−
1962− keyRelease(Qt.Key_Alt);
1963− }
1964−
19651935 function test_focusAppFromLauncherExitsSpread_data() {
19661936 return [
19671937 {tag: "autohide launcher", launcherLocked: false },
@@ -1972,7 +1942,7 @@ Rectangle {
19721942 function test_focusAppFromLauncherExitsSpread(data) {
19731943 loadDesktopShellWithApps()
19741944 var launcher = findChild(shell, "launcher");
1975− var desktopSpread = findChild(shell, "spread");
1945+ var stage = findChild(shell, "stage");
19761946 var bfb = findChild(launcher, "buttonShowDashHome");
19771947
19781948 GSettingsController.setAutohideLauncher(!data.launcherLocked);
@@ -1981,7 +1951,7 @@ Rectangle {
19811951 keyPress(Qt.Key_Alt)
19821952 keyClick(Qt.Key_Tab);
19831953
1984− tryCompare(desktopSpread, "state", "altTab")
1954+ tryCompare(stage, "state", "spread")
19851955
19861956 if (!data.launcherLocked) {
19871957 revealLauncherByEdgePushWithMouse();
@@ -1994,7 +1964,7 @@ Rectangle {
19941964 if (!data.launcherLocked) {
19951965 tryCompare(launcher, "state", "")
19961966 }
1997− tryCompare(desktopSpread, "state", "")
1967+ tryCompare(stage, "state", "windowed")
19981968
19991969 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash")
20001970
@@ -2164,10 +2134,10 @@ Rectangle {
21642134
21652135 // Sanity checking
21662136 if (data.usageScenario === "tablet") {
2167− var app1Delegate = findChild(shell, "spreadDelegate_" + app1SurfaceId);
2137+ var app1Delegate = findChild(shell, "appDelegate_" + app1SurfaceId);
21682138 compare(app1Delegate.stage, ApplicationInfoInterface.MainStage);
21692139
2170− var app2Delegate = findChild(shell, "spreadDelegate_" + app2SurfaceId);
2140+ var app2Delegate = findChild(shell, "appDelegate_" + app2SurfaceId);
21712141 compare(app2Delegate.stage, ApplicationInfoInterface.MainStage);
21722142 }
21732143 verify(!app1.isTouchApp);
@@ -2205,10 +2175,10 @@ Rectangle {
22052175
22062176 // Sanity checking
22072177 if (data.usageScenario === "tablet") {
2208− var app1Delegate = findChild(shell, "spreadDelegate_" + app1SurfaceId);
2178+ var app1Delegate = findChild(shell, "appDelegate_" + app1SurfaceId);
22092179 compare(app1Delegate.stage, ApplicationInfoInterface.MainStage);
22102180
2211− var app2Delegate = findChild(shell, "spreadDelegate_" + app2SurfaceId);
2181+ var app2Delegate = findChild(shell, "appDelegate_" + app2SurfaceId);
22122182 compare(app2Delegate.stage, ApplicationInfoInterface.MainStage);
22132183 }
22142184
@@ -2295,19 +2265,19 @@ Rectangle {
22952265 function test_superTabToCycleLauncher(data) {
22962266 loadShell("desktop");
22972267 shell.usageScenario = "desktop";
2268+ waitForRendering(shell);
22982269 GSettingsController.setAutohideLauncher(!data.launcherLocked);
22992270 waitForRendering(shell);
23002271
2272+ var stage = findChild(shell, "stage");
23012273 var launcher = findChild(shell, "launcher");
23022274 var launcherPanel = findChild(launcher, "launcherPanel");
23032275 var firstAppInLauncher = LauncherModel.get(0).appId;
2304−
23052276 compare(launcher.state, data.launcherLocked ? "visible": "");
23062277 compare(launcherPanel.highlightIndex, -2);
23072278 compare(ApplicationManager.focusedApplicationId, "unity8-dash");
23082279
23092280 // Use Super + Tab Tab to cycle to the first entry in the launcher
2310− wait(1000);
23112281 keyPress(Qt.Key_Super_L, Qt.MetaModifier);
23122282 keyClick(Qt.Key_Tab);
23132283 tryCompare(launcher, "state", "visible");
@@ -2328,6 +2298,7 @@ Rectangle {
23282298 tryCompare(launcher, "state", data.launcherLocked ? "visible" : "");
23292299 tryCompare(launcherPanel, "highlightIndex", -2);
23302300 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
2301+ tryCompare(stage, "focus", true)
23312302 }
23322303
23332304 function test_longpressSuperOpensLauncherAndShortcutsOverlay() {
@@ -2388,6 +2359,7 @@ Rectangle {
23882359 waitForRendering(shell);
23892360 var appContainer = findChild(shell, "appContainer");
23902361 var launcher = findChild(shell, "launcher");
2362+ var launcherPanel = findChild(launcher, "launcherPanel");
23912363
23922364 var appSurfaceId = topLevelSurfaceList.nextId;
23932365 var app = ApplicationManager.startApplication("music-app");
@@ -2398,10 +2370,12 @@ Rectangle {
23982370 waitForRendering(shell);
23992371
24002372 GSettingsController.setAutohideLauncher(true);
2373+ tryCompare(launcherPanel, "x", -launcher.panelWidth)
24012374 waitForRendering(shell)
24022375 var hiddenSize = appDelegate.width;
24032376
24042377 GSettingsController.setAutohideLauncher(false);
2378+ tryCompare(launcherPanel, "x", 0)
24052379 waitForRendering(shell)
24062380 var shownSize = appDelegate.width;
24072381
@@ -2439,9 +2413,9 @@ Rectangle {
24392413
24402414 function test_inputEventsOnEdgesEndUpInAppSurface_data() {
24412415 return [
2442− { tag: "phone", repeaterName: "spreadRepeater" },
2443− { tag: "tablet", repeaterName: "spreadRepeater" },
2444− { tag: "desktop", repeaterName: "appRepeater" },
2416+ { tag: "phone" },
2417+ { tag: "tablet" },
2418+ { tag: "desktop" },
24452419 ]
24462420 }
24472421
@@ -2456,7 +2430,7 @@ Rectangle {
24562430 var app = ApplicationManager.startApplication("camera-app");
24572431 waitUntilAppWindowIsFullyLoaded(appSurfaceId);
24582432
2459− var appRepeater = findChild(shell, data.repeaterName);
2433+ var appRepeater = findChild(shell, "appRepeater");
24602434 var topmostAppDelegate = appRepeater.itemAt(0);
24612435 verify(topmostAppDelegate);
24622436
@@ -2731,5 +2705,41 @@ Rectangle {
27312705 tryCompare(topLevelSurfaceList, "count", countBeforeClose - 1);
27322706 tryCompareFunction(function() { return ApplicationManager.focusedApplicationId; }, "calendar-app");
27332707 }
2708+
2709+ function test_oskDisplacesWindow_data() {
2710+ return [
2711+ {tag: "no need to displace", windowHeight: units.gu(10), windowY: units.gu(5), targetDisplacement: units.gu(5), oskEnabled: true},
2712+ {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight, oskEnabled: true},
2713+ {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight, oskEnabled: true},
2714+ {tag: "osk not on this screen", windowHeight: units.gu(40), windowY: units.gu(10), targetDisplacement: units.gu(10), oskEnabled: false},
2715+ ]
2716+ }
2717+
2718+ function test_oskDisplacesWindow(data) {
2719+ loadShell("desktop");
2720+ shell.usageScenario = "desktop";
2721+ waitForRendering(shell);
2722+ swipeAwayGreeter();
2723+ shell.oskEnabled = data.oskEnabled;
2724+
2725+ var oldOSKState = SurfaceManager.inputMethodSurface.state;
2726+ SurfaceManager.inputMethodSurface.state = Mir.RestoredState;
2727+ var appRepeater = findChild(shell, "appRepeater");
2728+ var dashAppDelegate = appRepeater.itemAt(0);
2729+ verify(dashAppDelegate);
2730+ dashAppDelegate.requestedHeight = data.windowHeight;
2731+ dashAppDelegate.requestedY = data.windowY;
2732+ SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
2733+ var initialY = dashAppDelegate.y;
2734+ print("intial", initialY, "panel", PanelState.panelHeight);
2735+ verify(initialY > PanelState.panelHeight);
2736+
2737+ SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, root.height / 2, root.width, root.height / 2));
2738+ tryCompare(dashAppDelegate, "y", data.targetDisplacement);
2739+
2740+ SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
2741+ tryCompare(dashAppDelegate, "y", initialY);
2742+ SurfaceManager.inputMethodSurface.state = oldOSKState;
2743+ }
27342744 }
27352745}
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