@@ -32,6 +32,21 @@ Rectangle {
32 property bool spreadEnabled: true // If false, animations and right edge will be disabled
32 property bool spreadEnabled: true // If false, animations and right edge will be disabled
33 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
33 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
34 property int orientation: Qt.PortraitOrientation
34 property int orientation: Qt.PortraitOrientation
35+ property QtObject applicationManager: ApplicationManager
36+ property bool focusFirstApp: true // If false, focused app will appear on right edge like other apps
37+ property bool altTabEnabled: true
38+ property real startScale: 1.1
39+ property real endScale: 0.7
41+ // How far left the stage has been dragged
42+ readonly property real dragProgress: spreadRepeater.count > 0 ? -spreadRepeater.itemAt(0).xTranslate : 0
44+ readonly property alias dragging: spreadDragArea.dragging
46+ // Only used by the tutorial right now, when it is teasing the right edge
47+ property real dragAreaOverlap
36 color: "#111111"
51 color: "#111111"
@@ -52,30 +67,30 @@ Rectangle {
52 if (inverseProgress == 0 && priv.oldInverseProgress > 0) {
67 if (inverseProgress == 0 && priv.oldInverseProgress > 0) {
53 // left edge drag released. Minimum distance is given by design.
68 // left edge drag released. Minimum distance is given by design.
54 if (priv.oldInverseProgress > units.gu(22)) {
69 if (priv.oldInverseProgress > units.gu(22)) {
55− ApplicationManager.requestFocusApplication("unity8-dash");
70+ applicationManager.requestFocusApplication("unity8-dash");
58 priv.oldInverseProgress = inverseProgress;
73 priv.oldInverseProgress = inverseProgress;
61 Connections {
76 Connections {
62− target: ApplicationManager
77+ target: applicationManager
64 onFocusRequested: {
79 onFocusRequested: {
65 if (spreadView.phase > 0) {
80 if (spreadView.phase > 0) {
66 spreadView.snapTo(priv.indexOf(appId));
81 spreadView.snapTo(priv.indexOf(appId));
68− ApplicationManager.focusApplication(appId);
83+ applicationManager.focusApplication(appId);
72 onApplicationAdded: {
87 onApplicationAdded: {
73 if (spreadView.phase == 2) {
88 if (spreadView.phase == 2) {
74− spreadView.snapTo(ApplicationManager.count - 1);
89+ spreadView.snapTo(applicationManager.count - 1);
76 spreadView.phase = 0;
91 spreadView.phase = 0;
77 spreadView.contentX = -spreadView.shift;
92 spreadView.contentX = -spreadView.shift;
78− ApplicationManager.focusApplication(appId);
93+ applicationManager.focusApplication(appId);
@@ -90,9 +105,9 @@ Rectangle {
92 function focusTopMostApp() {
107 function focusTopMostApp() {
93− if (ApplicationManager.count > 0) {
108+ if (applicationManager.count > 0) {
94− var topmostApp = ApplicationManager.get(0);
109+ var topmostApp = applicationManager.get(0);
95− ApplicationManager.focusApplication(topmostApp.appId);
110+ applicationManager.focusApplication(topmostApp.appId);
@@ -100,18 +115,19 @@ Rectangle {
100 QtObject {
115 QtObject {
118+ readonly property int firstSpreadIndex: root.focusFirstApp ? 1 : 0
103− property string focusedAppId: ApplicationManager.focusedApplicationId
119+ property string focusedAppId: applicationManager.focusedApplicationId
104− property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
120+ property var focusedApplication: applicationManager.findApplication(focusedAppId)
105 property var focusedAppDelegate: null
121 property var focusedAppDelegate: null
107 property real oldInverseProgress: 0
123 property real oldInverseProgress: 0
108− property bool animateX: true
124+ property bool animateX: false
110 onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
126 onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
112 function indexOf(appId) {
128 function indexOf(appId) {
113− for (var i = 0; i < ApplicationManager.count; i++) {
129+ for (var i = 0; i < applicationManager.count; i++) {
114− if (ApplicationManager.get(i).appId == appId) {
130+ if (applicationManager.get(i).appId == appId) {
115 return i;
131 return i;
@@ -131,7 +147,7 @@ Rectangle {
132 // This indicates when the spreadView is active. That means, all the animations
148 // This indicates when the spreadView is active. That means, all the animations
133 // are activated and tiles need to line up for the spread.
149 // are activated and tiles need to line up for the spread.
134− readonly property bool active: shiftedContentX > 0 || spreadDragArea.status === DirectionalDragArea.Recognized
150+ readonly property bool active: shiftedContentX > 0 || spreadDragArea.status === DirectionalDragArea.Recognized || !root.focusFirstApp
136 // The flickable needs to fill the screen in order to get touch events all over.
152 // The flickable needs to fill the screen in order to get touch events all over.
137 // However, we don't want to the user to be able to scroll back all the way. For
153 // However, we don't want to the user to be able to scroll back all the way. For
@@ -197,10 +213,17 @@ Rectangle {
197 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
213 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
198 snapAnimation.targetContentX = width * positionMarker4 + 1 - shift;
214 snapAnimation.targetContentX = width * positionMarker4 + 1 - shift;
199 snapAnimation.start();
215 snapAnimation.start();
202 function snapTo(index) {
219 function snapTo(index) {
203− if (ApplicationManager.count <= index) {
220+ if (!root.altTabEnabled) {
221+ // Reset to start instead
222+ snapAnimation.targetContentX = -shift;
223+ snapAnimation.start();
226+ if (applicationManager.count <= index) {
204 // In case we're trying to snap to some non existing app, lets snap back to the first one
227 // In case we're trying to snap to some non existing app, lets snap back to the first one
205 index = 0;
228 index = 0;
@@ -215,10 +238,12 @@ Rectangle {
215 snapAnimation.start();
238 snapAnimation.start();
218− // In case the ApplicationManager already holds an app when starting up we're missing animations
241+ // In case the applicationManager already holds an app when starting up we're missing animations
219 // Make sure we end up in the same state
242 // Make sure we end up in the same state
220 Component.onCompleted: {
243 Component.onCompleted: {
221 spreadView.contentX = -spreadView.shift
244 spreadView.contentX = -spreadView.shift
245+ priv.animateX = true;
246+ snapAnimation.complete();
224 SequentialAnimation {
249 SequentialAnimation {
@@ -235,7 +260,7 @@ Rectangle {
235 ScriptAction {
260 ScriptAction {
236 script: {
261 script: {
237 if (spreadView.selectedIndex >= 0) {
262 if (spreadView.selectedIndex >= 0) {
238− ApplicationManager.focusApplication(ApplicationManager.get(spreadView.selectedIndex).appId);
263+ applicationManager.focusApplication(applicationManager.get(spreadView.selectedIndex).appId);
240 spreadView.selectedIndex = -1;
265 spreadView.selectedIndex = -1;
241 spreadView.phase = 0;
266 spreadView.phase = 0;
@@ -250,7 +275,7 @@ Rectangle {
250 // This width controls how much the spread can be flicked left/right. It's composed of:
275 // This width controls how much the spread can be flicked left/right. It's composed of:
251 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
276 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
252 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
277 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
253− width: Math.max(3, ApplicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
278+ width: Math.max(3, applicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
254 height: parent.height
279 height: parent.height
255 Behavior on width {
280 Behavior on width {
256 enabled: spreadView.closingIndex >= 0
281 enabled: spreadView.closingIndex >= 0
@@ -265,20 +290,22 @@ Rectangle {
265 x: spreadView.contentX
290 x: spreadView.contentX
267 onClicked: {
292 onClicked: {
293+ if (root.altTabEnabled) {
268− spreadView.snapTo(0);
294+ spreadView.snapTo(0);
271 Repeater {
298 Repeater {
272 id: spreadRepeater
299 id: spreadRepeater
273 objectName: "spreadRepeater"
300 objectName: "spreadRepeater"
274− model: ApplicationManager
301+ model: applicationManager
275 delegate: TransformedSpreadDelegate {
302 delegate: TransformedSpreadDelegate {
276 id: appDelegate
303 id: appDelegate
277 objectName: "appDelegate" + index
304 objectName: "appDelegate" + index
278 startAngle: 45
305 startAngle: 45
279 endAngle: 5
306 endAngle: 5
280− startScale: 1.1
307+ startScale: root.startScale
281− endScale: 0.7
308+ endScale: root.endScale
282 startDistance: spreadView.tileDistance
309 startDistance: spreadView.tileDistance
283 endDistance: units.gu(.5)
310 endDistance: units.gu(.5)
284 width: spreadView.width
311 width: spreadView.width
@@ -286,11 +313,12 @@ Rectangle {
286 selected: spreadView.selectedIndex == index
313 selected: spreadView.selectedIndex == index
287 otherSelected: spreadView.selectedIndex >= 0 && !selected
314 otherSelected: spreadView.selectedIndex >= 0 && !selected
288 interactive: !spreadView.interactive && spreadView.phase === 0
315 interactive: !spreadView.interactive && spreadView.phase === 0
289− && spreadView.shiftedContentX === 0 && root.interactive && index === 0
316+ && spreadView.shiftedContentX === 0 && root.interactive && isFocused
290− swipeToCloseEnabled: spreadView.interactive && !snapAnimation.running
317+ swipeToCloseEnabled: spreadView.interactive && root.interactive && !snapAnimation.running
291 maximizedAppTopMargin: root.maximizedAppTopMargin
318 maximizedAppTopMargin: root.maximizedAppTopMargin
292 dropShadow: spreadView.active ||
319 dropShadow: spreadView.active ||
293 (priv.focusedAppDelegate && priv.focusedAppDelegate.x !== 0)
320 (priv.focusedAppDelegate && priv.focusedAppDelegate.x !== 0)
321+ focusFirstApp: root.focusFirstApp
295 readonly property bool isDash: model.appId == "unity8-dash"
323 readonly property bool isDash: model.appId == "unity8-dash"
@@ -298,7 +326,7 @@ Rectangle {
300 // focused app is always positioned at 0 except when following left edge drag
328 // focused app is always positioned at 0 except when following left edge drag
301− if (index == 0) {
329+ if (isFocused) {
302 if (!isDash && root.inverseProgress > 0 && spreadView.phase === 0) {
330 if (!isDash && root.inverseProgress > 0 && spreadView.phase === 0) {
303 return root.inverseProgress;
331 return root.inverseProgress;
@@ -309,10 +337,10 @@ Rectangle {
311 // Otherwise line up for the spread
339 // Otherwise line up for the spread
312− return spreadView.width + (index - 1) * spreadView.tileDistance;
340+ return spreadView.width + spreadIndex * spreadView.tileDistance;
315− application: ApplicationManager.get(index)
343+ application: applicationManager.get(index)
316 closeable: !isDash
344 closeable: !isDash
318 property real behavioredIndex: index
346 property real behavioredIndex: index
@@ -350,7 +378,7 @@ Rectangle {
350 progress: {
378 progress: {
351 var tileProgress = (spreadView.shiftedContentX - behavioredIndex * spreadView.tileDistance) / spreadView.width;
379 var tileProgress = (spreadView.shiftedContentX - behavioredIndex * spreadView.tileDistance) / spreadView.width;
352 // Tile 1 needs to move directly from the beginning...
380 // Tile 1 needs to move directly from the beginning...
353− if (behavioredIndex == 1 && spreadView.phase < 2) {
381+ if (root.focusFirstApp && behavioredIndex == 1 && spreadView.phase < 2) {
354 tileProgress += spreadView.tileDistance / spreadView.width;
382 tileProgress += spreadView.tileDistance / spreadView.width;
356 // Limiting progress to ~0 and 1.7 to avoid binding calculations when tiles are not
384 // Limiting progress to ~0 and 1.7 to avoid binding calculations when tiles are not
@@ -365,7 +393,7 @@ Rectangle {
366 // This mostly is the same as progress, just adds the snapping to phase 1 for tiles 0 and 1
394 // This mostly is the same as progress, just adds the snapping to phase 1 for tiles 0 and 1
367 animatedProgress: {
395 animatedProgress: {
368− if (spreadView.phase == 0 && index < 2) {
396+ if (spreadView.phase == 0 && index <= priv.firstSpreadIndex) {
369 if (progress < spreadView.positionMarker1) {
397 if (progress < spreadView.positionMarker1) {
370 return progress;
398 return progress;
371 } else if (progress < spreadView.positionMarker1 + 0.05){
399 } else if (progress < spreadView.positionMarker1 + 0.05){
@@ -390,11 +418,11 @@ Rectangle {
392 onClicked: {
420 onClicked: {
393− if (spreadView.phase == 2) {
421+ if (root.altTabEnabled && spreadView.phase == 2) {
394− if (ApplicationManager.focusedApplicationId == ApplicationManager.get(index).appId) {
422+ if (applicationManager.focusedApplicationId == applicationManager.get(index).appId) {
395 spreadView.snapTo(index);
423 spreadView.snapTo(index);
397− ApplicationManager.requestFocusApplication(ApplicationManager.get(index).appId);
425+ applicationManager.requestFocusApplication(applicationManager.get(index).appId);
@@ -409,7 +437,7 @@ Rectangle {
410 onClosed: {
438 onClosed: {
411 spreadView.closingIndex = index;
439 spreadView.closingIndex = index;
412− ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
440+ applicationManager.stopApplication(applicationManager.get(index).appId);
@@ -422,7 +450,7 @@ Rectangle {
422 direction: Direction.Leftwards
450 direction: Direction.Leftwards
423 enabled: (spreadView.phase != 2 && root.spreadEnabled) || dragging
451 enabled: (spreadView.phase != 2 && root.spreadEnabled) || dragging
425− anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
453+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom; rightMargin: -root.dragAreaOverlap }
426 width: root.dragAreaWidth
454 width: root.dragAreaWidth
428 property var gesturePoints: new Array()
456 property var gesturePoints: new Array()